码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 5343 MZL's Circle Zhou SAM

时间:2016-07-18 02:08:54      阅读:301      评论:0      收藏:0      [点我收藏+]

标签:

MZL‘s Circle Zhou

题意:给定两个长度不超过a,b(1 <= |a|,|b| <= 90000),x为a的连续子串,b为y的连续子串(x和y均可以是空串);问x+y形成的不同串的个数?

误区:开始一门心思想着求出总的可形成的字符串个数,再减去a,b中相同的子串重叠的部分;想通过连续插入a+b得到的SAM并不能获得信息;因为x,y是任意的子串,连续插入导致一定是a的后缀和b的前缀

 

正解:直接在计算有不同子串时,就不去计算重复的 <=>对于一个可能出现x后缀和y前缀相同而重复计算的情况,就全部加入x而y变为去掉前缀的部分

即找到x最后一个字符ch,使得在x后能加的字符串不以ch开头,这样就避免了重复的情况;(思想很好啊!!!)

对SAM的理解:

SAM将字符串插入之后,step[np] - step[pre[np]]表示 状态np比父节点pre[np]多出的以相同的点为后缀的不同子串的个数;

对SAM进行拓扑之后:

<1>如果按照字符串先遍历有效节点,初始化每个状态出现的次数为1,就可从孩子节点得到父节点的出现的次数;再使用step得到不同子串的数目即可求解一类与出现次数有关的问题;如 hdu 4641 K-string

 

<2>如果初始化root为1,递推出每个节点出现的次数 cnt[ g[p][v] ] += cnt[p]将得到的是从初始状态到当前状态的所有子串(当然是不同的)数;

本题:需要对a字符串构成的SAM的每一个状态(节点)找出g[u][j] == 0(即上面讲的x不可能有j这条状态转移边),这时如果y以j开头的不同子串的数量已知,那就可以直接往结果中贡献出 cnt[u] * C[j]的值(cnt[u]表示a字符串以x状态为后缀不同子串的个数,C[j]表示y中以j开头子串的数目)

 

 cnt集合根据上面的性质2可以直接递推得到,那C集合呢?

其实就是在找到一个cnt[u]之后,如果u有一条j的转移边j,那么C[j]的值就增加了cnt[u]?

 

还有一个问题:SAM只能求得以某个点为后缀的,怎么得到从每个点开始的呢?

那就将b逆序插入。。(好思想啊)这样后缀就变成了后缀里面的前缀

 

对于b的各种情况已经计算完了,但是由于可以有空子串,a的不同子串还需加上;

坑:这道题数据很大,怀疑是% unsigned long long判的结果,使用long long  直接WA了

注:如果C集合写在SAM里面,在调用SA时运算的SA的C集合。。不是SB的

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 using namespace std;
  5 typedef unsigned long long ll;
  6 
  7 const int maxn = 100007;
  8 const int SIGMA_SIZE = 26;
  9 
 10 struct SAM{
 11     int sz, last, pos[maxn<<1];
 12     int g[maxn<<1][SIGMA_SIZE], pre[maxn<<1], step[maxn<<1];
 13     ll cnt[maxn<<1];
 14 
 15     void init(){
 16         sz = 0;last = 1;
 17         newNode(0);
 18     }
 19 
 20     void newNode(int s){
 21         pre[++sz] = 0;
 22         step[sz] = s;
 23         memset(g[sz],0,sizeof(g[sz]));
 24     }
 25 
 26     int idx(char ch){ return ch - a; }
 27 
 28     void Insert(char ch);
 29     void topoSort();
 30     void preSolve(ll* C);
 31     ll solve(ll* C);
 32 
 33 }SA, SB;
 34 
 35 void SAM::Insert(char ch){
 36     newNode(step[last] + 1);
 37     int v = idx(ch), np = sz, p = last;
 38     while(p && !g[p][v]){
 39         g[p][v] = np;
 40         p = pre[p];
 41     }
 42 
 43     if(p){
 44         int q = g[p][v];
 45         if(step[q] == step[p] + 1)
 46             pre[np] = q;
 47         else{
 48             newNode(step[p] + 1);
 49             int nq = sz;
 50             for(int i = 0;i < SIGMA_SIZE;i++)
 51                 g[nq][i] = g[q][i];
 52 
 53             pre[nq] = pre[q];
 54             pre[q] = pre[np] = nq;
 55 
 56             while(p && g[p][v] == q)
 57                 g[p][v] = nq, p = pre[p];
 58         }
 59     }
 60     else pre[np] = 1;
 61     last = np;
 62 }
 63 
 64 void SAM::topoSort(){
 65     for(int i = 0; i <= sz; i++) cnt[i] = 0;
 66     for(int i = 1; i <= sz; i++) cnt[step[i]]++;
 67     for(int i = 1; i <= sz; i++) cnt[i] += cnt[i-1];
 68     for(int i = 1; i <= sz; i++) pos[cnt[step[i]]--] = i;
 69 }
 70 
 71 void SAM::preSolve(ll* C){
 72     topoSort();
 73     for(int i = 0;i <= sz; i++) cnt[i] = 0;
 74     cnt[1] = 1;
 75 
 76     for(int i = 1;i <= sz; i++){  //从root递推到每个节点出现的次数
 77         int u = pos[i];
 78         for(int j = 0; j < SIGMA_SIZE; j++){
 79             int v = g[u][j];
 80             if(v == 0) continue;
 81             C[j] += cnt[u];
 82             cnt[v] += cnt[u];
 83         }
 84     }
 85 }
 86 
 87 ll SAM::solve(ll *C){
 88     topoSort();
 89     for(int i = 0; i <= sz; i++) cnt[i] = 0;
 90     cnt[1] = 1;
 91 
 92     ll ret = 0;
 93     for(int i = 1; i <= sz; i++){
 94         int u = pos[i];
 95         for(int j = 0; j < SIGMA_SIZE; j++){
 96             int v = g[u][j];
 97             if(v == 0) ret += cnt[u] * C[j];
 98             else cnt[v] += cnt[u];
 99          }
100          ret += cnt[u];    // x + ""
101     }
102     return ret;
103 }
104 
105 char str[maxn];
106 int main()
107 {
108     int T;
109     scanf("%d",&T);
110     while(T--){
111         scanf("%s", str);
112         int n = strlen(str);
113         SA.init();
114         for(int i = 0;i < n; i++)
115             SA.Insert(str[i]);
116 
117         scanf("%s", str);
118         n = strlen(str);
119         SB.init();
120         for(int i = n - 1; i >= 0; i--)    // **
121             SB.Insert(str[i]);
122 
123         ll C[SIGMA_SIZE];
124         memset(C,0,sizeof(C));
125         SB.preSolve(C);
126 
127         printf("%llu\n",SA.solve(C));
128     }
129 }

 

hdu 5343 MZL's Circle Zhou SAM

标签:

原文地址:http://www.cnblogs.com/hxer/p/5679721.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!