4566: [Haoi2016]找相同字符 SAM

折腾了好久。不过收获还是很多的。第一次自己去画SAM所建出来fail树。深入体会了这棵树的神奇性质。

当然,我最终靠着自己A掉了。(这是我第一次推SAM的性质(以前都是抄别人的,感觉自己好可耻),不过感觉好像是摸着黑行走啊!)

这道题,可以先对第一个串建出后缀自动机。然后第二个串在后缀自动机上跑。

首先,SAM所建出的fail树的性质有:

1: 树上一个节点对应了多个串,串的个数是 len[x] - len[fa[x]], 同时它们都出现了 sz[x] 次(感觉好像说不大清,可以看一下代码对于sz数组的处理)。

2: 设串的长度为 l 那么每个节点 x 的 sz[x] * (len[x] - len[fa[x]]) 的和为 (l+1) * l / 2

3:   树上一个节点的所有后代所代表的串都是以这个节点代表的串为后缀的串。

4:   根据性质3推出: 如果一个字符串匹配到一个树上的某个节点, 那么这个节点的所有祖先所代表的所有祖先都是它的子串,但那个节点本身所代表的所以串并不一定都是他的子串。

 

所以这道题的ans应该呼之欲出了。

(说了这么多,你们知道解题的思路是什么吗?显然对于子串的问题,我们肯定是假设当前匹配到第i个字符的时候,把以第i个字符结尾的所有字串都处理掉。) 

那么假设当前匹配到 i 字符时转移到了节点x, 那么x对答案的贡献就是 祖先所代表的所有的串(可以预处理)和  匹配到 i 字符时,(第二个串与第一个串的最长匹配长度-len[fa[x]] )*sz[x] 

第二个串与第一个串的最长匹配长度-len[fa[x]](这个就是第二个串在x节点内所能匹配的子串数)

 

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define rep(i,j,k) for(register int i = j; i <= k; i++)
 5 #define dow(i,j,k) for(register int i = j; i >= k; i--)
 6 #define ez(i,j) for(edge*i = head[j]; i; i=i->next)
 7 #define maxn 200005
 8 #define ll long long
 9 using namespace std;
10 
11 struct edge{ int to; edge*next; } e[maxn<<1], *pt = e, *head[maxn<<1];
12 inline void add(int x,int y) { pt->to = y, pt->next = head[x], head[x] = pt++; }
13 
14 int last = 1, u, v, nv, p, tot = 1, sz[maxn<<1], dep[maxn<<1], fa[maxn<<1], son[maxn<<1][26];
15 inline void extend(int c) {
16     u = last; p = last = ++tot; dep[p] = dep[u] + 1; sz[p] = 1;
17     while( u && !son[u][c] ) son[u][c] = p, u = fa[u];
18     if( !u ) fa[p] = 1;
19     else {
20         v = son[u][c];
21         if( dep[v] == dep[u] + 1 ) fa[p] = v;
22         else {
23             nv = ++tot; dep[nv] = dep[u] + 1;
24             fa[nv] = fa[v]; fa[v] = fa[p] = nv;
25             memcpy(son[nv],son[v],sizeof(son[v]));
26             while( son[u][c] == v ) son[u][c] = nv, u = fa[u]; 
27         }
28     } 
29 }
30 
31 int q[maxn<<1], tong[maxn<<1];
32 inline void pre() {
33     rep(i,1,tot) tong[dep[i]]++;
34     rep(i,1,tot) tong[i] += tong[i-1];
35     dow(i,tot,1) q[tong[dep[i]]--] = i;
36     int x;
37     dow(i,tot,1) x = q[i], sz[fa[x]] += sz[x];
38 
39 }
40 char c1[maxn]; ll dp[maxn<<1];
41 #define to i->to
42 inline void dfs(int x) {
43     dp[x] += 1ll * sz[x] * (dep[x] - dep[fa[x]]);
44     ez(i,x) dp[to] += dp[x], dfs(to);
45 }
46 
47 int main() {
48     scanf("%s", c1); int s1 = strlen(c1);
49     rep(i,0,s1-1) extend(c1[i]-'a');  
50     pre(); rep(i,1,tot) if( fa[i] ) add(fa[i],i); dfs(1);
51     //rep(i,1,tot) { rep(j,0,5) cout<<son[i][j]<<" "; cout<<endl; }
52     //rep(i,1,tot) cout<<sz[i]<<" "<<dp[i]<<" "<<dep[i]-dep[fa[i]]<<" "<<fa[i]<<endl;
53     scanf("%s", c1); s1 = strlen(c1);
54     int t, p = 1, l = 0; ll ans = 0;
55     rep(i,0,s1-1) {
56         t = c1[i] - 'a';
57         while( p && !son[p][t] ) p = fa[p];
58         if( !p ) p = 1, l = 0;
59         else l = min(l,dep[p]) + 1, p = son[p][t];
60         ans += dp[fa[p]], ans += 1ll * (l - dep[fa[p]]) * sz[p];
61     }
62     cout<<ans<<endl;
63     return 0;
64 }

 

人一我十,人十我万!追逐青春的梦想,怀着自信的心,永不放弃!仿佛已看到希望,尽管还在远方

posted on 2016-05-11 21:05  83131  阅读(762)  评论(0编辑  收藏  举报

导航