http://hihocoder.com/problemset/problem/1413?sid=1199641
这题断断续续做了2个多星期吧,一直不会
设总答案为sum,替换后新加的子串数量为x,失去的是y,那么每个位置的答案就是sum + x[i] - y[i]
首先可以知道如果把某个位置设置成'#',那么肯定有i * (len - i + 1)个新的不同的子串
比如是aa#cb,左边有i个选择,右边有len - i + 1个选择,根据组合数学就是i * (len - i + 1)个不同的子串
然后替换过后,就会有一些原本有的子串被删除了。
对于每一个状态,可以拓扑出它的mxpos和mipos也就是endpos的两个位置。
那么对于一个长度是len的子串,是否删除 了这个字符后 在整个字符串中不再出现,可以这样判断
如果mxpos - len + 1(也就是这个长度是len的子串的最大开始位置),如果这个位置还 < mipos那么如果删除了这个位置
肯定会丢失这个长度是len的字符串了。可以画个图吧看看
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; const int maxn = 3e5 + 20, N = 26; struct SAM { int mxCnt[maxn << 1], son[maxn << 1][N], fa[maxn << 1], pos[maxn << 1]; int flag[maxn << 1][3]; //是否前缀节点 int mi[maxn << 1], mx[maxn << 1]; int root, last, DFN, t; int create() { ++t; mxCnt[t] = pos[t] = fa[t] = NULL; // mi[t] = inf, mx[t] = -inf; for (int i = 0; i < 3; ++i) flag[t][i] = NULL; for (int i = 0; i < N; ++i) son[t][i] = NULL; return t; } void init() { ++DFN; t = 0, root = 1; last = create(); } void addChar(int x, int _pos, int id) { // _pos表示在原串中的位置 int p = last; int np = create(); last = np; mxCnt[np] = mxCnt[p] + 1, pos[np] = _pos, flag[np][id] = DFN; //前缀节点 for (; p && son[p][x] == NULL; p = fa[p]) son[p][x] = np; if (p == NULL) { fa[np] = root; return; } int q = son[p][x]; if (mxCnt[q] == mxCnt[p] + 1) { fa[np] = q; return; } int nq = create(); //用来代替q的,默认不是前缀节点 flag[nq][id] = DFN - 1; //默认不是前缀节点 pos[nq] = pos[q]; //pos要和q相同 for (int i = 0; i < N; ++i) son[nq][i] = son[q][i]; fa[nq] = fa[q], mxCnt[nq] = mxCnt[p] + 1; fa[q] = nq, fa[np] = nq; for (; p && son[p][x] == q; p = fa[p]) son[p][x] = nq; } int dp[maxn << 1], in[maxn << 1], que[maxn << 1]; void topo() { //多次使用不用清空 for (int i = 2; i <= t; ++i) { in[fa[i]]++; mi[i] = mx[i] = pos[i]; } int head = 0, tail = 0; for (int i = 2; i <= t; ++i) { if (in[i] == 0) que[tail++] = i; } while (head < tail) { int cur = que[head++]; if (cur == root) break; mx[fa[cur]] = max(mx[fa[cur]], mx[cur]); in[fa[cur]]--; if (in[fa[cur]] == 0) que[tail++] = fa[cur]; } } } sam; LL cnt[maxn], sub[maxn]; void add(int be, int en, LL val, LL d) { cnt[be] += val; cnt[en + 1] -= d * (en - be) + val; sub[be + 1] += d; sub[en + 1] -= d; } void init(int en) { for (int i = 1; i <= en; ++i) { sub[i] += sub[i - 1]; cnt[i] += cnt[i - 1] + sub[i]; } } char str[maxn]; void work() { int len; cin >> len; sam.init(); scanf("%s", str + 1); LL ans = 0; for (int i = 1; str[i]; ++i) { sam.addChar(str[i] - 'a', i, 0); } sam.topo(); // int fuck = 5; // printf("%d ", sam.mx[fuck + 1]); for (int i = 2; i <= sam.t; ++i) { ans += sam.mxCnt[i] - sam.mxCnt[sam.fa[i]]; if (sam.mx[i] - sam.mxCnt[i] + 1 <= sam.mi[i]) { int be = sam.mx[i] - sam.mxCnt[i] + 1; int en = min(sam.mx[i] - sam.mxCnt[sam.fa[i]], sam.mi[i]); int len = en - be + 1; // printf("%d %d ", be, en); add(be, en, 1, 1); be = en + 1; en = sam.mi[i]; //还有一段,要全部加上len个 // printf("%d %d ", be, en); if (be <= en) add(be, en, len, 0); } } init(len); // printf("%d ", cnt[4]); for (int i = 1; i <= len; ++i) { printf("%lld ", ans + 1LL * (i) * (len - i + 1) - cnt[i]); } } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }