bzoj3473 字符串

双倍经验:bzoj3277

题意:给定n个字符串,对于每个字符串,求有多少个非空子串是其中至少k个字符串的子串。

解:有一种毒瘤后缀数组解法...不过后缀自动机吊打后缀树组!耶~

广义后缀自动机。

先建出来,然后对于每个串,跑一遍后缀自动机。我们试图把该串的所有子串都打上标记(sum++)。

跑到的每个节点,都代表一个前缀,我们跳fail就能得到该前缀的所有后缀。用vis数组避免重复标记。

所有sum >= k的节点,贡献为len - len[fail],否则为0。

之后对于每个串再跑后缀自动机,跑到的每个节点的贡献为它到根的贡献,可以预处理出来,加起来就是答案。

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cstring>
  4 #include <string>
  5 
  6 const int N = 500010;
  7 
  8 struct Edge {
  9     int nex, v;
 10 }edge[N << 1]; int top;
 11 
 12 int tr[N][26], len[N], cnt[N], sum[N], fail[N], vis[N];
 13 int last = 1, tot = 1, Time, k, e[N];
 14 std::string str[100010];
 15 char s[N];
 16 
 17 inline void add(int x, int y) {
 18     top++;
 19     edge[top].v = y;
 20     edge[top].nex = e[x];
 21     e[x] = top;
 22     return;
 23 }
 24 
 25 inline int split(int p, int f) {
 26     int Q = tr[p][f], nQ = ++tot;
 27     len[nQ] = len[p] + 1;
 28     fail[nQ] = fail[Q];
 29     fail[Q] = nQ;
 30     memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
 31     while(tr[p][f] == Q) {
 32         tr[p][f] = nQ;
 33         p = fail[p];
 34     }
 35     return nQ;
 36 }
 37 
 38 inline int insert(int p, char c) {
 39     int f = c - 'a';
 40     if(tr[p][f]) {
 41         int Q = tr[p][f];
 42         if(len[Q] == len[p] + 1) {
 43             return Q;
 44         }
 45         return split(p, f);
 46     }
 47     int np = ++tot;
 48     len[np] = len[p] + 1;
 49     while(p && !tr[p][f]) {
 50         tr[p][f] = np;
 51         p = fail[p];
 52     }
 53     if(!p) {
 54         fail[np] = 1;
 55     }
 56     else {
 57         int Q = tr[p][f];
 58         if(len[Q] == len[p] + 1) {
 59             fail[np] = Q;
 60         }
 61         else {
 62             fail[np] = split(p, f);
 63         }
 64     }
 65     return np;
 66 }
 67 
 68 void DFS(int x) {
 69     if(vis[x] == Time || !x) {
 70         return;
 71     }
 72     sum[x]++;
 73     vis[x] = Time;
 74     DFS(fail[x]);
 75     return;
 76 }
 77 
 78 void DFS_1(int x) {
 79     cnt[x] = cnt[fail[x]] + (len[x] - len[fail[x]]) * (sum[x] >= k);
 80     //printf("cnt %d = %d + %d * %d \n", x, cnt[fail[x]], len[x] - len[fail[x]], sum[x] >= k);
 81     for(int i = e[x]; i; i = edge[i].nex) {
 82         int y = edge[i].v;
 83         DFS_1(y);
 84     }
 85     return;
 86 }
 87 
 88 int main() {
 89     int n;
 90     scanf("%d%d", &n, &k);
 91     for(int i = 1; i <= n; i++) {
 92         scanf("%s", s);
 93         int t = strlen(s);
 94         last = 1;
 95         for(int j = 0; j < t; j++) {
 96             last = insert(last, s[j]);
 97         }
 98         str[i] = (std::string)(s);
 99     }
100 
101     for(int i = 1; i <= n; i++) {
102         int p = 1, t = str[i].size();
103         Time = i;
104         for(int j = 0; j < t; j++) {
105             int f = str[i][j] - 'a';
106             p = tr[p][f];
107             DFS(p);
108         }
109     }
110 
111     for(int i = 2; i <= tot; i++) {
112         add(fail[i], i);
113         //printf("add %d %d len = %d %d \n", fail[i], i, len[fail[i]], len[i]);
114     }
115     DFS_1(1);
116 
117     for(int i = 1; i <= n; i++) {
118         int p = 1, t = str[i].size(), ans = 0;
119         //printf("i = %d \n", i);
120         for(int j = 0; j < t; j++) {
121             int f = str[i][j] - 'a';
122             p = tr[p][f];
123             ans += cnt[p];
124             //printf(" > p = %d ans += %d \n", p, cnt[p]);
125         }
126         printf("%d ", ans);
127     }
128 
129     return 0;
130 }
AC代码

 

转载于:https://www.cnblogs.com/huyufeifei/p/10233034.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值