2017 暑假艾教集訓 day10 AC自動機+馬拉車+後綴數組 +kmp

https://vjudge.net/contest/177933#overview

H HDU3068

做法:馬拉車模板

#include <bits/stdc++.h>
using namespace std;
const int maxn= 110015;
char s[maxn];
char str[maxn*2];
int  dp[maxn*2];
int mlc()
{
    int n=strlen(s+1);
    str[0]='@';
    for(int i=2;i<=2*n;i+=2)
    {
        str[i-1] = '#' ;
        str[i]=s[i/2] ;
    }
    str[2*n+1] = '#';
    str[2*n+2] = '0';
    str[2*n+3] = 0;
    memset(dp,0,sizeof(dp));

    int ans=0 ,mx=0,id=0;
    for(int i=1;i<=2*n+1;++i)
    {
        mx>i? dp[i]=min(dp[2*id-i],mx-i) :dp[i]=1 ;
        while(str[i-dp[i]] == str[i+dp[i]]) dp[i]++;
        if(dp[i]+i>mx)
        {
            mx=dp[i]+i;
            id=i;
        }
        ans=max(ans,dp[i]);
    }
    return ans-1;
}

int main()
{
    while(scanf("%s",s+1)!=EOF)
    {
        printf("%d\n",mlc());
    }
    return 0;
}

POJ 2406

做法: KMP  最小循環節 N-NEXT[N] 前提(N%(N-NEXT[N])==0)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=1e6+10;
char s[maxn];
int next[maxn];
int len;
void getnext()
{
    int j,k;
    j=0;  next[0] =  k =-1;
    while(j<len)
    {
        if(k==-1 || s[j]==s[k]) next[++j]=(++k);
        else  k=next[k];
    }
}
int main()
{
    while(scanf("%s",s)!=EOF)
    {
        if(s[0]=='.') break;
        len=strlen(s);
        getnext();
        int temp = len-next[len];
        if(len%temp==0)
        {
            printf("%d\n",len/temp);
        }
        else
        {
            printf("1\n");
        }
    }
    return 0;
}

HDU 2222

做法:AC自動機模板

#include <bits/stdc++.h>
using namespace std;

struct trie
{
    int next[500010][26],fail[500010],end[500010];
    int root , tot;
    int newnode()
    {
        for(int i=0;i<26;++i)
        {
            next[tot][i]=-1;
        }
        end[tot++] = 0;
        return tot-1;
    }
    void init()
    {
        tot=0;
        root = newnode();
    }
    void push(char str[])
    {
        int len=strlen(str);
        int now=root;
        for(int i=0;i<len;++i)
        {
            if(next[now][str[i]-'a']== -1)
            {
                next[now][str[i]-'a'] = newnode();
            }
            now = next[now][str[i]-'a'];
        }
        end[now]++;
    }
    void build()
    {
        queue<int> que;
        fail[root] = root;
        for(int i=0;i<26;++i)
        {
            if(next[root][i]==-1) next[root][i]=root;
            else
            {
                fail[next[root][i]]=root;
                que.push(next[root][i]);
            }
        }
        while(!que.empty())
        {
            int now=que.front() ; que.pop();
            for(int i=0;i<26;++i)
            {
                if(next[now][i]==-1)
                {
                    next[now][i]=next[fail[now]][i];
                }
                else
                {
                    fail[next[now][i]]=next[fail[now]][i];
                    que.push(next[now][i]);
                }
            }
        }
    }
    int query(char str[])
    {
        int len=strlen(str);
        int now=root;
        int ans=0;
        for(int i=0;i<len;++i)
        {
            now= next[now][str[i]-'a'];
            int temp = now;
            while(temp!=root)
            {
                ans+=end[temp];
                end[temp]=0;
                temp=fail[temp];
            }
        }
        return ans;
    }
}ac;
char s[1000010];
int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        ac.init();
        for(int i=0;i<n;++i)
        {
            scanf("%s",s);
            ac.push(s);
        }
        ac.build();
        scanf("%s",s);
        printf("%d\n",ac.query(s));
    }
    return 0;
}

POJ 2778

做法:只需要把病毒串標記。然後對沒有標記在矩陣上記錄權值(如果next節點不存在也要記錄),套一個矩陣快速幕即可!

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const ll mod =1e5;
struct matirx
{
     ll mu[105][105];
     matirx()
     {
         for(int i=0;i<105;++i)
         {
             for(int j=0;j<105;++j)
             {
                 mu[i][j]=0;
             }
         }
     }
};

int turn(char c)
{
    if(c=='A') return 0;    if(c=='G') return 1;
    if(c=='C') return 2;    if(c=='T') return 3;
}
struct tire
{
    int next[105][4],fail[105],end[105];
    int root,tot;
    int newnode()
    {
        for(int i=0;i<4;++i)
        {
            next[tot][i]=-1;
        }
        end[tot++]=0;
        return tot-1;
    }
    void init()
    {
        tot=0;
       root=newnode();
    }
    void push(char str[])
    {
        int len=strlen(str);
        int now=root;
        for(int i=0;i<len;++i)
        {
            if(next[now][turn(str[i])]==-1)
            {
                next[now][turn(str[i])] = newnode();
            }
            now=next[now][turn(str[i])];
        }
        end[now]=1;
    }
    void build()
    {
        queue<int> que;
        fail[root] = root;
        for(int i=0;i<4;++i)
        {
            if(next[root][i]==-1)
            {
                next[root][i]=root;
            }
            else
            {
                fail[next[root][i]] = root;
                que.push(next[root][i]);
            }
        }
        while(!que.empty())
        {
             int now=que.front() ;que.pop();
             if(end[fail[now]]) end[now]=1;
             for(int i=0;i<4;++i)
             {
                 if(next[now][i]==-1)
                 {
                     next[now][i]=next[fail[now]][i];
                 }
                 else
                 {
                     fail[next[now][i]] = next[fail[now]][i];
                     que.push(next[now][i]);
                 }
             }
        }
    }
}ac;
matirx muilt(matirx a ,matirx b)
{
    matirx ans;
    for(int i=0;i<ac.tot;++i)
    {
        for(int k=0;k<ac.tot;++k)
        {
            for(int j=0;j<ac.tot;++j)
            {
                ans.mu[i][j] = ( ans.mu[i][j] + a.mu[i][k] * b.mu[k][j] )%mod;;
            }
        }
    }
    return ans;
}
matirx pow(matirx bit ,ll n)
{
    matirx ans;
    for(int i=0;i<ac.tot;++i) ans.mu[i][i]=1;
    while(n)
    {
        if( n & 1) ans = muilt(ans,bit);
        bit = muilt(bit,bit);
        n>>=1;
    }
    return ans;
}
matirx getbit()
{
    matirx x;
    for(int i=0;i<ac.tot;++i)
    {
        for(int j=0;j<4;++j)
        {
            if(!ac.end[ac.next[i][j]])
            {
                x.mu[i][ac.next[i][j]]++;
            }
        }
    }
    return x;
}
char s[100500];
int main()
{
    int n;
    ll m;
    while(scanf("%d%I64d",&n,&m)!=EOF)
    {
        ac.init();
        for(int i=0;i<n;++i)
        {
            scanf("%s",s);
            ac.push(s);
        }
        ac.build();
        matirx bit = getbit();
        matirx ans = pow(bit,m);
        ll sum=0;
        for(int i=0;i<ac.tot;++i)
        {
            sum = (sum+ans.mu[0][i])%mod;
        }
        printf("%lld\n",sum);
    }
    return 0;
}

HDU 2852

做法:AC自動機 + 狀壓dp

AC自動機上維護狀態轉移,對每個key進行表號,並用s表示,注意如果fail鏈指向標記也要進行標記

#include <bits/stdc++.h>
using namespace std;
int n,m,k;
int num[1<<12];
int dp[30][105][1<<12];
const int mod=20090717;
struct tire
{
     int next[105][26] , end[105] ,fail[105];
     int tot,root;
     int newnode()
     {
         for(int i=0;i<26;++i) next[tot][i]=-1;
         end[tot++]=0;
         return tot-1;
     }
     void init()
     {
         tot=0;
         root = newnode();
     }
     void pusuh(char s[],int cnt)
     {
         int len=strlen(s);
         int now=root;
         for(int i=0;i<len;++i)
         {
             if(next[now][s[i]-'a']==-1) next[now][s[i]-'a'] = newnode();
             now = next[now][s[i]-'a'];
         }
         end[now]|=(1<<cnt);
     }
     void build()
     {
         fail[root]=root;
         queue<int> que;
         for(int i=0;i<26;++i)
         {
            if(next[root][i]==-1) next[root][i] = root;
            else
            {
                 fail[next[root][i]] = root;
                 que.push(next[root][i]);
            }
         }
         while(!que.empty())
         {
             int now=que.front(); que.pop();
             end[now]|=end[fail[now]];

             for(int i=0;i<26;++i)
             {
                 if(next[now][i]==-1)
                 {
                     next[now][i]=next[fail[now]][i];
                 }
                 else
                 {
                     fail[next[now][i]] = next[fail[now]][i];
                     que.push(next[now][i]);
                 }
             }
         }
     }
     int work()
    {
       for(int i=0;i<=n;++i)
       {
           for(int j=0;j<tot;++j)
           {
               for(int k=0;k<(1<<m);++k)
               {
                   dp[i][j][k]=0;
               }
           }
       }
       dp[0][0][0]=1;
       for(int i=0;i<n;++i)
       {
           for(int j=0;j<tot;++j)
           {
               for(int k=0;k<(1<<m);++k)
               {
                   if(!dp[i][j][k]) continue;
                    for(int t=0;t<26;++t)
                   {
                       dp[i+1][next[j][t]][k|end[next[j][t]]] = ( dp[i+1][next[j][t]][k|end[next[j][t]]]+dp[i][j][k] )%mod;
                   }
               }
           }
       }
       int ans=0;
       for(int i=0;i<(1<<m);++i)
       {
           if(num[i]<k) continue;
           for(int j=0;j<tot;++j)
                ans= (ans+dp[n][j][i])%mod;
       }
       return ans;
    }

}ac;
char s[105];
int main()
{
    memset(num,0,sizeof(num));
    for(int i=0;i<(1<<10);++i)
    {
        for(int j=0;j<10;++j)
        {
            if(i&(1<<j)) num[i]++;
        }
    }
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        if(n==0&&m==0&&k==0) break;
        ac.init();
        for(int i=0;i<m;++i)
        {
            scanf("%s",s);
            ac.pusuh(s,i);
        }
        ac.build();
        printf("%d\n",ac.work());
    }
    return 0;
}


HDU 2774
做法:後綴數組:將兩個串放在一起,然後在中間加一個標記即可。答案就在 rank排名的交界處 ,一定要滿足兩個sa在不同的串內, 然後求最大的height

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=5e5+10;
char s1[maxn],s2[maxn],s[maxn<<1];
int sa[maxn],t1[maxn],t2[maxn],c[maxn<<1];
int rk[maxn],height[maxn];
void getsa(int n,int m)
{
    int *x=t1,*y=t2;
    for(int i=0;i<m;++i) c[i]=0;
    for(int i=0;i<n;++i) c[x[i]=s[i]]++;
    for(int i=1;i<m;++i) c[i]+=c[i-1];
    for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k<<=1)
    {
        int p=0;
        for(int i=n-k;i<n;++i) y[p++]=i;
        for(int i=0;i<n;++i)
        if(sa[i]>=k)  y[p++] = sa[i]-k;

        for(int i=0;i<m;++i) c[i]=0;
        for(int i=0;i<n;++i) c[x[y[i]]]++;
        for(int i=1;i<m;++i) c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;    x[sa[0]]=0;
        for(int i=1;i<n;++i)
        x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
        if(p>=n) break;
        m=p;
    }
}
void gethight(int n)
{
    int k=0;
    for(int i=0;i<n;++i) rk[sa[i]]=i;
    for(int i=0;i<n;++i)
    {
        if(k) k--;
        else k=0;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k]) k++;
        height[rk[i]]=k;
    }
}

int main()
{
    while(scanf("%s%s",s1,s2)!=EOF)
    {
        int pos=0;
        for(int i=0;s1[i];++i) s[pos++] = s1[i]-'a'+1;
        s[pos++]=28;
        for(int i=0;s2[i];++i) s[pos++] = s2[i]-'a'+1;
        s[pos++]=0;
        getsa(pos,29);
        gethight(pos);
        int ans=0, len = strlen(s1);
        for(int i=2;s[i];++i)
        {
            if(height[i]>ans)
            {
                if(sa[i-1]>=0 &&sa[i-1]<len&&len<sa[i]) ans=height[i];
                if(0<=sa[i] && sa[i]<len &&len<sa[i-1]) ans=height[i];
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


POJ 3261
求重複k次以上的最長重複子串
做法:套完板子之後,二分答案,將heigh大於mid的分爲一組如果有超過k個則檢驗成功

板子使用注意一些地方

getsa(n+1,maxn+2);

m基數取比最大的數稍大即可。最後串加個s[N]=0; hight是從2開始的!!!
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn=20010;
int s[maxn<<1];
int sa[maxn],t1[maxn],t2[maxn],c[maxn<<1];
int rk[maxn],height[maxn];
void getsa(int n,int m)
{
    int *x=t1,*y=t2;
    for(int i=0;i<m;++i) c[i]=0;
    for(int i=0;i<n;++i) c[x[i]=s[i]]++;
    for(int i=1;i<m;++i) c[i]+=c[i-1];
    for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k<<=1)
    {
        int p=0;
        for(int i=n-k;i<n;++i) y[p++]=i;
        for(int i=0;i<n;++i)
        if(sa[i]>=k)  y[p++] = sa[i]-k;

        for(int i=0;i<m;++i) c[i]=0;
        for(int i=0;i<n;++i) c[x[y[i]]]++;
        for(int i=1;i<m;++i) c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;    x[sa[0]]=0;
        for(int i=1;i<n;++i)
        x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
        if(p>=n) break;
        m=p;
    }
}
void gethight(int n)
{
    int k=0,j;
    for(int i=0;i<=n;++i) rk[sa[i]]=i;
    for(int i=0;i<n;++i)
    {
        if(k) k--;
        j=sa[rk[i]-1];
        while(s[i+k]==s[j+k]) k++;
        height[rk[i]]=k;
    }
}
bool judge(int mid,int n,int kk)
{
    int num=1;
    for(int i=2;i<=n;++i)
    {
        if(height[i]>=mid)
        {
            num++;
            if(num>=kk) return 1;
        }
        else num=1;
    }
    return 0;
}

int main()
{
    int n,kk;
    while(scanf("%d%d",&n,&kk)!=EOF)
    {
        int maxn=0;
        for(int i=0;i<n;++i)  { scanf("%d",&s[i]); maxn=max(maxn,s[i]);}
        s[n]=0;
        getsa(n+1,maxn+2);
        gethight(n);
        int l=0,r=n;
        int ans=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(judge(mid,n,kk)) {ans = max(ans,mid); l=mid+1;}
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章