网上有篇题解写的是线段树合并维护求值?
题目描述
有一个只包含小写字母,长度为 $n$ 的字符串 $S$ 。有一些字母是好的,剩下的是坏的。
定义一个子串 $S_{l\ldots r}$是好的,当且仅当这个子串包含不超过 $k$ 个坏的字母。
求有多少个不同的满足以下要求的字符串 $T$ :
- $T$ 作为 $S$ 的子串出现过。
- 存在一个 $T$ 出现的位置 $[l,r]$ ,满足 $S_{l\ldots r}$ 是好的。
输入格式
第一行有一个字符串 $S$ 。
第二行有一个字符串 $B$ 。若 $B_i=‘1’$ 则表示 $S_i$ 是好的,否则表示 $S_i$ 是坏的。
第三行有一个整数 $k$ 。
输出格式
一个整数:答案。
数据范围与提示
子任务 $1$($10$ 分):$n\leq 10$。
子任务 $2$($10$ 分):$n\leq 100$。
子任务 $3$($10$ 分):$n\leq 1000$。
子任务 $4$($10$ 分):$n\leq 100000,k=n$。
子任务 $5$($10$ 分):$n\leq 100000,k=0$。
子任务 $6$($20$ 分):$n\leq 100000$,若$S_i=S_j$,则$B_i=B_j$。
子任务 $7$($30$ 分):$n\leq 100000$。
对于 $100\%$ 的数据:$1\leq n\leq {10}^5,0\leq k\leq {10}^5$, $S$ 只包含小写字母。
题目来源:全是水题的GDOI模拟赛 by yww
题目分析
定位:比较模板的SAM题(然而我一个月前并不会SAM)
题意即求满足一定条件的若干个字符串里本质不同的子串个数。当然这里的“一定条件”比较特殊,是连续的一段子串。(如果这里的“一定条件”字符串是若干个互不相干的串,似乎就需要“广义后缀自动机”来处理了)
既然对于每一个新增的节点,其合法的子串都有一个左边界,那么在SAM里处理的时候,就可以对每一节点加一个权值$mx[u]$表示该节点的最长合法扩展长度。这个权值的作用就在于建完自动机后的$calc()$,原先数本质不同的子串个数是这样的: ans += len[p]-len[fa[p]]; 现在就是 ans += std::max(std::min(mx[p], len[p])-len[fa[p]], 0); 。记得注意一下extend里对mx的转移。
1 #include2 const int maxn = 200035; 3 4 int n,lim,sum[maxn],size[maxn],cnt[maxn],pos[maxn]; 5 long long ans; 6 struct SAM 7 { 8 int ch[maxn][27],fa[maxn],len[maxn],mx[maxn],lst,tot; 9 void init()10 {11 lst = tot = 1;12 }13 void extend(int c, int v)14 {15 int p = lst, np = ++tot;16 lst = np, len[np] = len[p]+1, mx[np] = v; //len[p+1] Here 居然打成标红的这个17 for (; p&&!ch[p][c]; p=fa[p]) ch[p][c] = np;18 if (!p) fa[np] = 1;19 else{20 int q = ch[p][c];21 if (len[p]+1==len[q]) fa[np] = q;22 else{23 int nq = ++tot;24 len[nq] = len[p]+1, mx[nq] = mx[q];25 memcpy(ch[nq], ch[q], sizeof ch[q]);26 fa[nq] = fa[q], fa[q] = fa[np] = nq;27 for (; p&&ch[p][c]==q; p=fa[p])28 ch[p][c] = nq;29 }30 }31 }32 void calc()33 {34 for (int i=1; i<=tot; i++) ++cnt[len[i]];35 for (int i=1; i<=tot; i++) cnt[i] += cnt[i-1];36 for (int i=1; i<=tot; i++) pos[cnt[len[i]]] = i, --cnt[len[i]];37 for (int i=tot; i; i--)38 {39 int p = pos[i];40 mx[fa[p]] = std::max(mx[fa[p]], mx[p]); 41 ans += std::max(std::min(mx[p], len[p])-len[fa[p]], 0);42 }43 }44 }f;45 char s[maxn],t[maxn];46 47 int main()48 {49 scanf("%s%s%d",s+1,t+1,&lim);50 n = strlen(s+1);51 f.init();52 for (int i=1, j=0; i<=n; i++)53 {54 sum[i] = (t[i]=='0')+sum[i-1];55 while (sum[i]-sum[j] > lim) ++j;56 f.extend(s[i]-'a'+1, i-j);57 }58 f.calc();59 printf("%lld\n",ans);60 return 0;61 }
END