【SAM】loj#6401. 字符串

网上有篇题解写的是线段树合并维护求值?

题目描述

  有一个只包含小写字母,长度为 $n$ 的字符串 $S$ 。有一些字母是好的,剩下的是坏的。

  定义一个子串 $S_{lldots r}$是好的,当且仅当这个子串包含不超过 $k$ 个坏的字母。

  求有多少个不同的满足以下要求的字符串 $T$ :

  • $T$ 作为 $S$ 的子串出现过。
  • 存在一个 $T$ 出现的位置 $[l,r]$ ,满足 $S_{lldots r}$​ 是好的。

输入格式

  第一行有一个字符串 $S$ 。

  第二行有一个字符串 $B$ 。若 $B_i=‘1’$ 则表示 $S_i$​ 是好的,否则表示 $S_i$ 是坏的。

  第三行有一个整数 $k$ 。

输出格式

  一个整数:答案。

数据范围与提示

  子任务 $1$($10$ 分):$nleq 10$。

  子任务 $2$($10$ 分):$nleq 100$。

  子任务 $3$($10$ 分):$nleq 1000$。

  子任务 $4$($10$ 分):$nleq 100000,k=n$。

  子任务 $5$($10$ 分):$nleq 100000,k=0$。

  子任务 $6$($20$ 分):$nleq 100000$,若$S_i=S_j$​,则$B_i=B_j$​。

  子任务 $7$($30$ 分):$nleq 100000$。

  对于 $100\%$ 的数据:$1leq nleq {10}^5,0leq kleq {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 #include<bits/stdc++.h>
 2 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
",ans);
60      return 0;
61 }

END

原文地址:https://www.cnblogs.com/antiquality/p/10511165.html