• HDU 4622 Reincarnation


     题目大意:

    给定一个基础字符串,再给多个字符串,求出基础字符串中能得到的不一样的子串的个数未出现在后面多个字符串当中

    这里以基础子串构建后缀自动机是没问题的

    后面的多个子串不断在后缀自动机上进行匹配,每次到达一个状态点,就要更新当前点所能达到的其他字符串抵达最大长度 mx,那么未能匹配的长度就是cur->l-mx

    因为父节点受子节点影响,所以要拓扑排序,不断更新父节点所能达到的最大长度,最后统计所有数量之和即可

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 using namespace std;
     5 
     6 #define N 100010
     7 #define ll long long
     8 struct SamNode{
     9     SamNode *son[26] , *f;
    10     int l , mx;
    11     void init(){
    12         for(int i=0 ; i<26 ; i++) son[i] = NULL;
    13         f=NULL;
    14         l = mx = 0;
    15     }
    16 }sam[N<<1] , *root , *last , *b[N<<1];
    17 
    18 int cnt , num[N];
    19 ll ret;
    20 char s[N];
    21 
    22 void add(int x)
    23 {
    24     SamNode *p = &sam[++cnt] , *jp = last;
    25     p->init();
    26     p->l = jp->l+1 ;
    27     last = p;
    28     for( ; jp&&!jp->son[x] ; jp=jp->f) jp->son[x] = p;
    29     if(!jp) p->f = root;
    30     else{
    31         if(jp->l+1 == jp->son[x]->l) p->f = jp->son[x];
    32         else{
    33             SamNode *r = &sam[++cnt] , *q = jp->son[x];
    34             r->init();
    35             *r = *q;
    36             r->l = jp->l+1;
    37             p->f = q->f = r;
    38             for( ; jp&&jp->son[x]==q ; jp=jp->f) jp->son[x]=r;
    39         }
    40     }
    41 }
    42 
    43 void solve(int n , int &cas)
    44 {
    45     int l , len=strlen(s);
    46     memset(num , 0 , sizeof(num));
    47     for(int i=0 ; i<=cnt ; i++) num[sam[i].l]++;
    48     for(int i=1 ; i<=len ; i++) num[i]+=num[i-1];
    49     for(int i=0 ; i<=cnt ; i++) b[--num[sam[i].l]]=&sam[i];
    50     for(int i=0 ; i<n ; i++){
    51         scanf("%s" , s);
    52         l=strlen(s) , len=0; //len表示当前所能达到的最大长度
    53         SamNode *cur = root;
    54         for(int j=0 ; j<l ; j++){
    55             int x = s[j]-'a';
    56             if(cur->son[x]){
    57                 len++;
    58                 cur = cur->son[x];
    59                 cur->mx = max(cur->mx , len);
    60             }else{
    61                 while(cur&&!cur->son[x]) cur=cur->f;
    62                 if(!cur)
    63                     cur = root , len=0;
    64                 else{
    65                     len = cur->l+1;
    66                     cur = cur->son[x];
    67                     cur->mx = max(cur->mx , len);
    68                 }
    69             }
    70         }
    71     }
    72     ll ret = 0;
    73     for(int i=cnt ; i>=1 ; i--){
    74        // cout<<i<<" "<<b[i]->l<<" "<<b[i]->mx<<endl;
    75         if(b[i]->mx) ret = ret + (b[i]->l-b[i]->mx>=0?b[i]->l-b[i]->mx:0);
    76         else ret = ret + b[i]->l-b[i]->f->l;
    77         b[i]->f->mx = max(b[i]->f->mx , b[i]->mx);
    78     }
    79     printf("Case %d: %I64d
    " , ++cas , ret);
    80 }
    81 
    82 int main()
    83 {
    84    // freopen("a.in" , "r", stdin);
    85     int T , n , cas=0;
    86     scanf("%d" , &T);
    87     while(T--)
    88     {
    89         scanf("%d%s" , &n , s);
    90         int l = strlen(s);
    91         sam[0].init();
    92         root = last = &sam[cnt=0] , ret = 0;
    93         for(int i=0 ; i<l ; i++)
    94             add(s[i]-'a');
    95         solve(n , cas);
    96     }
    97     return 0;
    98 }
  • 相关阅读:
    最快效率求出乱序数组中第k小的数
    调整数组顺序使奇数位于偶数前面
    分治算法的完美使用----归并排序
    快速排序分区以及优化方法
    分治法以及快速排序
    高效求a的n次幂的算法
    最长连续递增子序列(部分有序)
    在有空字符串的有序字符串数组中查找
    旋转数组的最小数字(改造二分法)
    递归----小白上楼梯
  • 原文地址:https://www.cnblogs.com/CSU3901130321/p/4596697.html
Copyright © 2020-2023  润新知