zoukankan      html  css  js  c++  java
  • hdu 5343 MZL's Circle Zhou SAM

    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
    ",SA.solve(C));
    128     }
    129 }
  • 相关阅读:
    Maven配置始终获取最新版本
    使用SpringBoot
    SpringBoot文档综述
    35_方法的使用
    34_结构体指针类型的使用
    33_同名字段的使用
    32_匿名字段的使用
    31_结构体的使用
    30_map的使用
    29_猜字游戏
  • 原文地址:https://www.cnblogs.com/hxer/p/5679721.html
Copyright © 2011-2022 走看看