bzoj4566 [Haoi2016]找相同字符

地址:http://www.lydsy.com/JudgeOnline/problem.php?id=4566

题目:

4566: [Haoi2016]找相同字符

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 600  Solved: 329

Description

给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。

 

Input

两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母

 

Output

输出一个整数表示答案

 

Sample Input

aabb
bbaa

Sample Output

10
 
思路:
  对第一个串建立后缀自动机,然后让第二个串在sam上跑。
  对于每个走到的状态p,fa[p]状态代表的后缀集合必然出现过,且p状态的长度为now-len[fa[p]](now为走到该状态的匹配长度)
  对于状态p,如果endpos(p)内的后缀全部出现过,则贡献为|endpos(p)|*(len[p]-len[fa[p]]),且要用fa[p]去更新p.
  把每个点的贡献记为sum[p]。
  则走到状态p,ans+=sum[fa[p]]+|endpos(p)|*(now-len[fa])。
 1 /**************************************************************
 2     Problem: 4566
 3     User: weeping
 4     Language: C++
 5     Result: Accepted
 6     Time:2072 ms
 7     Memory:50120 kb
 8 ****************************************************************/
 9  
10 #include <bits/stdc++.h>
11  
12 using namespace std;
13  
14 struct SAM
15 {
16     static const int MAXN = 200002<<1;//大小为字符串长度两倍
17     static const int LetterSize = 26;
18  
19     int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN];
20     int sum[MAXN], tp[MAXN], cnt[MAXN]; //sum,tp用于拓扑排序,tp为排序后的数组
21  
22     void init( void)
23     {
24         last = tot = 1;
25         len[1] = 0;
26         memset(ch,0,sizeof ch);
27         memset(fa,0,sizeof fa);
28         memset(cnt,0,sizeof cnt);
29     }
30  
31     void add( int x)
32     {
33         int p = last, np = last = ++tot;
34         len[np] = len[p] + 1, cnt[last] = 1;
35         while( p && !ch[p][x]) ch[p][x] = np, p = fa[p];
36         if( p == 0)
37             fa[np] = 1;
38         else
39         {
40             int q = ch[p][x];
41             if( len[q] == len[p] + 1)
42                 fa[np] = q;
43             else
44             {
45                 int nq = ++tot;
46                 memcpy( ch[nq], ch[q], sizeof ch[q]);
47                 len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq;
48                 while( p && ch[p][x] == q)  ch[p][x] = nq, p = fa[p];
49             }
50         }
51     }
52  
53     void toposort( void)
54     {
55         for(int i = 1; i <= len[last]; i++)   sum[i] = 0;
56         for(int i = 1; i <= tot; i++)   sum[len[i]]++;
57         for(int i = 1; i <= len[last]; i++)   sum[i] += sum[i-1];
58         for(int i = 1; i <= tot; i++)   tp[sum[len[i]]--] = i;
59         for(int i = tot; i; i--)   cnt[fa[tp[i]]] += cnt[tp[i]];
60     }
61  
62     void work(char *ss)
63     {
64         long long ans=0;
65         for(int i = 1; i <= tot; i++)
66             sum[tp[i]]=sum[fa[tp[i]]]+cnt[tp[i]]*(len[tp[i]]-len[fa[tp[i]]]);
67         for(int i=0,n=strlen(ss),p=1,num=0;i<n;i++)
68         {
69             int c=ss[i]-'a';
70             if(ch[p][c])    p=ch[p][c],num++;
71             else
72             {
73                 while(p&&!ch[p][c]) p=fa[p];
74                 if(!p)  p=1,num=0;
75                 else    num=len[p]+1,p=ch[p][c];
76             }
77             ans+=sum[fa[p]]+1LL*cnt[p]*(num-len[fa[p]]);
78         }
79         printf("%lld\n",ans);
80     }
81 } sam;
82  
83 char sa[200005],sb[200005];
84  
85 int main( void)
86 {
87     scanf("%s%s",sa,sb);
88     sam.init();
89     for(int i=0,len=strlen(sa);i<len;i++)   sam.add(sa[i]-'a');
90     sam.toposort(),sam.work(sb);
91     return 0;
92 }

 

posted @ 2017-09-15 13:44  weeping  阅读(159)  评论(0编辑  收藏  举报