CF645E Intellectual Inquiry

CF645E Intellectual Inquiry

给一个长为 \(m\) 的字符串 \(S\) ,你需要用第 \(1\)\(k\) 个小写字母构造一个长为 \(n\) 的字符串 \(S'\) ,使得 \(S+S'\) 本质不同的子串个数最多

输出子串个数 \(\pmod {10^9+7}\)

\(n,\ m\leq10^6,\ k\leq26\)

动态规划,贪心


考虑如何求出一个串本质不同的子串个数

设计一个线性dp,令 \(f_i\)\(S\)\(i\) 位所构成的子串个数

\(i\) 位有取或不取两种状态,因此若不考虑去重, \(f_i=2f_{i-1}\)

若选取第 \(i\) 位,计算重复的部分为以这个字符上一次出现的位置为结尾的子串个数(想一想,为什么)

综上所述 \(f_i=2f_{i-1}-f_{lst_i-1}\)

对于构造字符串 \(S'\) ,显然要让 \(f_{lst_i-1}\) 尽量小,由于 \(f_i\) 单调递增,于是只需让 \(lst_i\) 尽量小即可,可以用堆维护(实际上直接暴力 \(O(k)\) 常数还小一点

时间复杂度 \(O(n\log k)\)

代码

#include <bits/stdc++.h>
using namespace std;

typedef pair <int, int> pii;
const int maxn = 1e6 + 10, P = 1e9 + 7;
char str[maxn];
int n, m, k, lst[26], dp_buf[maxn << 1], *f = dp_buf + 1;

inline int add(int a, int b) {
  a += b; return a < P ? a : a - P;
}

inline int dec(int a, int b) {
  a -= b; return a < 0 ? a + P : a;
}

int main() {
  scanf("%d %d %s", &n, &k, str + 1);
  m = strlen(str + 1);
  f[0] = 1;
  for (int i = 1; i <= m; i++) {
    f[i] = dec(add(f[i - 1], f[i - 1]), f[lst[str[i] - 'a'] - 1]);
    lst[str[i] - 'a'] = i;
  }
  static priority_queue <int, vector <int>, greater <int> > Q;
  for (int i = 0; i < k; i++) {
    Q.push(lst[i]);
  }
  for (int i = m + 1; i <= n + m; i++) {
    int p = Q.top(); Q.pop();
    f[i] = dec(add(f[i - 1], f[i - 1]), f[p - 1]);
    Q.push(i);
  }
  printf("%d", f[n + m]);
  return 0;
}
posted @ 2019-06-01 18:14  cnJuanzhang  阅读(213)  评论(0编辑  收藏  举报