AC自动机专题小结

唔.....AC自动机的资料觉得大白书应该是很不错的资料的,再推荐一篇大牛的blog

http://blog.csdn.net/niushuai666/article/details/7002823

在理解了KMP之后相信AC自动机也不会有什么太大的问题

而与KMP之间的出入也就在于,KMP中可以直接定位具体是哪一个字符(即每一个节点都是代表了某一个确认的字符)

而AC自动机是以trie树为基础建立的,因此是通过确认某一个字符的指针是否非空来确定该字符是否存在(即可以把每一个点看做一个传送门集合地)

这样会不会好理解一点呢?(个人观点仅供参考)

下面仍然是题目

【HDU 2222】Keywords Search

很基础的AC自动机了,没什么好说的

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=500005;
int ch[SIZEN][26],sz;
int f[SIZEN],last[SIZEN];
int val[SIZEN],ret;
char str[SIZEN<<1];
int idx(char c){
    return c-'a';
}
void Insert(char s[]){
    int len=strlen(s);
    int rt=0;
    for(int i=0;i<len;i++)
    {
        int t=idx(s[i]);
        if(!ch[rt][t]){
            memset(ch[sz],0,sizeof(ch[sz]));
            ch[rt][t]=sz++;
        }
        rt=ch[rt][t];
    }
    val[rt]++;
}
void add(int j){
    while(j){
        ret+=val[j];
        val[j]=0;
        j=last[j];
    }
}
void Find(char s[]){
    int len=strlen(s);
    int j=0;
    for(int i=0;i<len;i++){
        int c=idx(s[i]);
        while(j&&!ch[j][c]) j=f[j];
        j=ch[j][c];
        if(val[j]) {add(j);}
         else if(last[j]) {add(last[j]);}
    }
}
void getfail(){
    queue<int> q;
    f[0]=0;
    for(int c=0;c<26;c++){
        int u=ch[0][c];
        if(u) {f[u]=0;q.push(u);last[u]=0;}
    }
    while(!q.empty()){
        int r=q.front();q.pop();
        for(int c=0;c<26;c++){
            int u=ch[r][c];
            if(!u) continue;
            q.push(u);
            int v=f[r];
            while(v&&!ch[v][c]) v=f[v];
            f[u]=ch[v][c];
            last[u]=val[f[u]]?f[u]:last[f[u]];
        }
    }
}
void init(){
    memset(val,0,sizeof(val));
    memset(ch[0],0,sizeof(ch[0]));
    ret=0;
    sz=1;
}
int main()
{
    //freopen("data.in","r",stdin);
    int i,j;
    int _,n;
    scanf("%d",&_);
    while(_--){
        init();
        scanf("%d",&n);
        for(i=0;i<n;i++)
        {
            scanf("%s",str);
            Insert(str);
        }
        getfail();
        scanf("%s",str);
        Find(str);
        printf("%d\n",ret);
    }
    return 0;
}
【HDU 2896】病毒侵袭

这道题的主要问题就是可见字符是包括空格的

因此要用gets

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=80005;
char str[SIZEN];
int ch[SIZEN][100],sz;
int f[SIZEN],last[SIZEN];
int val[SIZEN],flag[600];
void init(){
    memset(ch,0,sizeof(ch));
    memset(val,0,sizeof(val));
    sz=1;
}
int idx(char c){
    return c-32;
}
void Insert(char s[],int v){
    int len=strlen(str);
    int rt=0;
    for(int i=0;i<len;i++){
        int t=idx(s[i]);
        if(!ch[rt][t]){
            memset(ch[sz],0,sizeof(sz));
            ch[rt][t]=sz++;
        }
        rt=ch[rt][t];
    }
    val[rt]=v;
}
void Insert(char s[]){
    int len=strlen(s);
    int rt=0;
    for(int i=0;i<len;i++)
    {
        int t=idx(s[i]);
        if(!ch[rt][t]){
            memset(ch[sz],0,sizeof(ch[sz]));
            ch[rt][t]=sz++;
        }
        rt=ch[rt][t];
    }
    val[rt]++;
}
void getfail(){
    queue<int> q;
    f[0]=0;
    for(int c=0;c<100;c++){
        int u=ch[0][c];
        if(u) {f[u]=0;q.push(u);last[u]=0;}
    }
    while(!q.empty()){
        int r=q.front();q.pop();
        for(int c=0;c<100;c++){
            int u=ch[r][c];
            if(!u) {ch[r][c]=ch[f[r]][c];continue;}
            q.push(u);
            int v=f[r];
            while(v&&!ch[v][c]) v=f[v];
            f[u]=ch[v][c];
            last[u]=val[f[u]]?f[u]:last[f[u]];
        }
    }
}
void add(int j){
    while(j){
        flag[val[j]]=1;
        j=last[j];
    }
}
void Find(char s[]){
    int len=strlen(s);
    int j=0;
    for(int i=0;i<len;i++){
        int c=idx(s[i]);
        j=ch[j][c];
        if(val[j]) {add(j);}
         else if(last[j]) {add(last[j]);}
    }
}
void output(int id){
    printf("web %d: ",id);
    int tflag=0;
    for(int i=1;i<600;i++){
        if(tflag&&flag[i]) printf(" %d",i);
         else if(flag[i]){
            tflag=1;
            printf("%d",i);
         }
    }
    printf("\n");
}
int main()
{
    //freopen("data.in","r",stdin);
    int i,j;
    int n,m;
    int cnt;
    while(scanf("%d%*c",&n)!=EOF){
        init();
        for(i=0;i<n;i++){
            gets(str);
            Insert(str,i+1);
        }
        getfail();
        scanf("%d%*c",&m);
        cnt=0;
        for(i=0;i<m;i++){
            gets(str);
            memset(flag,0,sizeof(flag));
            Find(str);
            int tt=0;
            for(j=1;j<=n;j++){
                if(flag[j]){
                    tt=1;
                    break;
                }
            }
            if(tt){
                cnt++;
                output(i+1);
            }
        }
        printf("total: %d\n",cnt);
    }
    return 0;
}
【HDU 3065】 病毒侵袭持续中

还是模版题,写这种题的时候目的只是为了理解+熟悉模版

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=50005;
int ch[SIZEN][26],sz;
int f[SIZEN],last[SIZEN];
int val[SIZEN],cnt[1005];
char str[2000005];
char virus[1005][55];
void init(){
    memset(ch[0],0,sizeof(ch[0]));
    memset(val,0,sizeof(val));
    memset(cnt,0,sizeof(cnt));
    sz=1;
}
int idx(char c){
    return c-'A';
}
void Insert(char s[],int v){
    int len=strlen(s);
    int rt=0;
    for(int i=0;i<len;i++){
        int t=idx(str[i]);
        if(!ch[rt][t]){
            memset(ch[sz],0,sizeof(ch[sz]));
            ch[rt][t]=sz++;
        }
        rt=ch[rt][t];
    }
    val[rt]=v;
}
void add(int j){
    while(j){
        cnt[val[j]]++;
        j=last[j];
    }
}
void Find(char s[]){
    int len=strlen(s);
    int j=0;
    for(int i=0;i<len;i++){
        if(!(str[i]<='Z'&&str[i]>='A')){
            j=0;
            continue;
        }
        int c=idx(str[i]);
        while(j&&!ch[j][c]) j=f[j];
        j=ch[j][c];
        if(val[j]) add(j);
        else if(last[j]) add(last[j]);
    }
}
void getfail(){
    queue<int> q;
    f[0]=0;
    for(int c=0;c<26;c++){
        int u=ch[0][c];
        if(u){f[u]=0;q.push(u);last[u]=0;}
    }
    while(!q.empty()){
        int r=q.front();q.pop();
        for(int c=0;c<26;c++){
            int u=ch[r][c];
            if(!u) continue;
            q.push(u);
            int v=f[r];
            while(v&&!ch[v][c]) v=f[v];
            f[u]=ch[v][c];
            last[u]=val[f[u]]?f[u]:last[f[u]];
        }
    }
}
int main()
{
    //freopen("data.in","r",stdin);
    int i,j;
    int n;
    while(scanf("%d",&n)!=EOF){
        init();
        for(i=1;i<=n;i++){
            scanf("%s%*c",str);
            Insert(str,i);
            strcpy(virus[i],str);
        }
        getfail();
        gets(str);
        Find(str);
        for(i=1;i<=n;i++){
            if(cnt[i]) printf("%s: %d\n",virus[i],cnt[i]);
        }
    }
}

【HDU 2243】单词情结

感谢wzm学长对我的悉心教导,才让我能做出第一道AC自动机+dp

这道题一看数据L有2^31-1这么多,应该想到用矩阵加速

而需要求得是所有小于L满足条件的式子,因此需要增加一维来保存总的结果

至于矩阵的构造沿着AC自动机跑一遍就知道了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
const int SIZEN=40;
int ch[SIZEN][26],sz;
int f[SIZEN],val[SIZEN];
int cc[SIZEN],n;
char str[SIZEN];
struct Mat{
    ULL mat[SIZEN][SIZEN];
    void init(){
        memset(mat,0,sizeof(mat));
    }
    void output(){
        for(int i=0;i<=sz;i++){
            for(int j=0;j<=sz;j++) printf("%2d ",mat[i][j]);
            printf("\n");
        }
    }
    void getE(){
        memset(mat,0,sizeof(mat));
        for(int i=0;i<=sz;i++)
            mat[i][i]=1;
    }
};
Mat A;
Mat operator *(Mat a,Mat b){
    Mat ret;
    ret.init();
    int i,j,k;
    for(i=0;i<=sz;i++){
        for(j=0;j<=sz;j++)
            for(k=0;k<=sz;k++)
              ret.mat[i][j]+=a.mat[i][k]*b.mat[k][j];
    }
    return ret;
}
Mat pow(Mat a,LL p){
    Mat ret;
    ret.getE();
    while(p){
        if(p&1) ret=ret*a;
        a=a*a;
        p>>=1;
    }
    return ret;
}
int idx(char c){
    return c-'a';
}
void Insert(char s[],int v){
    int len=strlen(s);
    int rt=0;
    for(int i=0;i<len;i++){
        int t=idx(s[i]);
        if(!ch[rt][t]){
            memset(ch[sz],0,sizeof(ch[sz]));
            ch[rt][t]=sz++;
        }
        rt=ch[rt][t];
    }
    val[rt]=v;
}
void getfail(){
    queue<int> q;
    f[0]=0;
    for(int c=0;c<26;c++){
        int u=ch[0][c];
        if(u){f[u]=0;q.push(u);}
            else f[u]=0;
    }
    while(!q.empty()){
        int r=q.front();q.pop();
        if(val[f[r]]) val[r]=1;
        for(int c=0;c<26;c++){
            int u=ch[r][c];
            if(!u) {ch[r][c]=ch[f[r]][c];continue;}
            q.push(u);
            int v=f[r];
            while(v&&!ch[v][c]) v=f[v];
            f[u]=ch[v][c];
        }
    }
}
void init(){
    memset(ch[0],0,sizeof(ch[0]));
    memset(val,0,sizeof(val));
    sz=1;
}
int main()
{
    //freopen("data.in","r",stdin);
    int i,j;
    int n;
    LL l;
    while(scanf("%d %I64d",&n,&l)!=EOF){
        init();
        for(i=0;i<n;i++){
            scanf("%s",str);
            Insert(str,1);
        }
        getfail();
        //for(i=0;i<30;i++) printf("%d ",val[i]);printf("\n");
        A.init();
        for(i=0;i<sz;i++){
            if(val[i]) continue;
            for(j=0;j<26;j++){
               if(val[ch[i][j]]) A.mat[i][sz]++;
                else A.mat[i][ch[i][j]]++;
            }
        }
        A.mat[sz][sz]=26;
        sz++;A.mat[sz][sz]=A.mat[sz-1][sz]=1;
        //A.output();
        Mat ret=pow(A,l+1);
        printf("%I64u\n",ret.mat[0][sz]);
    }
    return 0;
}
/*
25 1 0 0  0
24 0 0 0  2
0  0 0 0  0
0  0 0 0  0
0  0 0 0 26*/
/*
25 1 0
24 0 2
0  0 26
*/
【HDU 2825】Wireless Password

看字符串的个数就可以想到应该用状压DP

然后可以先预处理出每一个状态有多少个单词

最后再统计一下单词数>=k的就get了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define MOD 20090717
using namespace std;
const int SIZEN=205;
int idx(char c){
    return c-'a';
}
int ch[SIZEN][26];
int dp[2][SIZEN][1040];
int inc[1040],sz,val[SIZEN];
int f[SIZEN];
char str[200];
void init(){
    memset(ch[0],0,sizeof(ch[0]));
    memset(val,0,sizeof(val));
    memset(dp,0,sizeof(dp));
    sz=1;
}
void Insert(char s[],int v){
    int len=strlen(s);
    int rt=0;
    for(int i=0;i<len;i++){
        int t=idx(s[i]);
        if(!ch[rt][t]){
            memset(ch[sz],0,sizeof(ch[sz]));
            ch[rt][t]=sz++;
        }
        rt=ch[rt][t];
    }
    val[rt]|=(1<<v);
}
void getfail(){
    queue<int> q;
    f[0]=0;
    for(int c=0;c<26;c++){
        int u=ch[0][c];
        if(u){f[u]=0;q.push(u);}
            else f[u]=0;
    }
    while(!q.empty()){
        int r=q.front();q.pop();
        val[r]=val[r]|val[f[r]];
        for(int c=0;c<26;c++){
            int u=ch[r][c];
            if(!u) {ch[r][c]=ch[f[r]][c];continue;}
                else{
                    f[u]=ch[f[r]][c];
                    q.push(u);
                }
        }
    }
}
void getinc(){
    for(int i=0;i<(1<<10);i++){
        int t=i;
        while(t){
            inc[i]+=t&1;
            t>>=1;
        }
    }
}
int main()
{
    //freopen("data.in","r",stdin);
    int i,j,k,l;
    int n,m,lim;
    int cnt=0;
    getinc();
    while(scanf("%d%d%d",&n,&m,&lim)!=EOF){
        if(!n&&!m&&!lim) break;
        init();
        cnt=1<<m;
        for(i=0;i<m;i++){
            scanf("%s",str);
            Insert(str,i);
        }
        getfail();
        dp[0][0][0]=1;
        for(i=0;i<n;i++){
            memset(dp[(i+1)%2],0,sizeof(dp[(i+1)%2]));
            for(j=0;j<sz;j++){
                for(l=0;l<cnt;l++){
                    if(dp[i%2][j][l]==0) continue;
                    for(k=0;k<26;k++){
                        int nj=ch[j][k];
                        int nl=val[nj]|l;
                        int& t_dp=dp[(i+1)%2][nj][nl];
                        t_dp=(t_dp+dp[i%2][j][l])%MOD;
                    }
                }
            }
        }
        int ans=0;
        for(int l=0;l<(1<<m);l++){
            if(inc[l]>=lim){
                for(j=0;j<sz;j++){
                    ans=(dp[n%2][j][l]+ans)%MOD;
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
【HDU 3341】Lost's revenge

(这道题我看了题解我会乱说?_(:з」∠)_)

ORZ之前一直没有想过这样的状态压缩,一直习惯于所有参与的个数都是相同因此同进制

这道题还是给了我很大的启发吧,学习了

其实题目也不会难,首先想到的便是记忆化搜索,但是时间卡得太紧做不到,因此改用递推就可过

重点是状态的保存。状态的压缩目的主要是为了几个参数之间能够不被相互干扰,因此想到用n进制来表示各个参数之间的状态

而这道题很明显用41进制会爆掉,再者A+C+G+T不会超过40,因此就用混合进制(_(:з」∠)_我乱取的名字不要打我)的方法保存就行了

代码略丑_(:з」∠)_,(刚尝试用结构体来写很好玩呀~后面一直都是结构体了)

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=505;
struct AC{
    int ch[SIZEN][4],sz;
    int f[SIZEN],val[SIZEN];
    char str[SIZEN];
    int pow[10],cnt[4];
    int dp[SIZEN][16000];
    void getpow(){
        pow[0]=1;
        for(int i=1;i<4;i++)
            pow[i]=pow[i-1]*(cnt[i-1]+1);
        pow[4]=10000000;
    }
    void init(){
        memset(ch[0],0,sizeof(ch[0]));
        memset(val,0,sizeof(val));
        memset(cnt,0,sizeof(cnt));
        sz=1;
    }
    int idx(char c){
        if(c=='A') return 0;
        else if(c=='C') return 1;
        else if(c=='G') return 2;
        else if(c=='T') return 3;
    }
    void insert(char s[],int v){
        int len=strlen(s);
        int rt=0;
        for(int i=0;i<len;i++){
            int t=idx(s[i]);
            if(!ch[rt][t]){
                memset(ch[sz],0,sizeof(ch[sz]));
                ch[rt][t]=sz++;
            }
            rt=ch[rt][t];
        }
        val[rt]+=v;
    }
    void getfail(){
        queue<int> q;
        f[0]=0;
        for(int c=0;c<4;c++){
            int u=ch[0][c];
            if(u) q.push(u);f[u]=0;
        }
        while(!q.empty()){
            int r=q.front();q.pop();
            if(val[f[r]]) val[r]+=val[f[r]];
            for(int c=0;c<4;c++){
                int u=ch[r][c];
                if(!u) {ch[r][c]=ch[f[r]][c];continue;}
                f[u]=ch[f[r]][c];
                q.push(u);
            }
        }
    }
    int dfs(int u,int s){
        if(dp[u][s]!=-1) return dp[u][s];
        int ans=0;
        for(int i=0;i<4;i++){
            if(s%pow[i+1]/pow[i]==0) continue;
            ans=max(ans,dfs(ch[u][i],s-pow[i]));
        }
        ans+=val[u];
        return dp[u][s]=ans;
    }
    int DP(int ss){
        dp[0][0]=0;
        for(int i=0;i<ss;i++){
            for(int j=0;j<sz;j++)
                if(dp[j][i]+1){
                for(int k=0;k<4;k++){
                    if((i%pow[k+1]/pow[k]+1)>cnt[k]) continue;
                    if(i+pow[k]>ss) continue;
                    int new_j=ch[j][k];
                    int new_i=i+pow[k];
                    int &t=dp[new_j][new_i];
                    t=max(t,dp[j][i]+val[new_j]);
                }
            }
        }
        int ans=0;
        for(int i=0;i<sz;i++) ans=max(ans,dp[i][ss]);
        return ans;
    }
    int solve(int n){
        init();
        for(int i=0;i<n;i++){
            scanf("%s",str);
            insert(str,1);
        }
        getfail();
        scanf("%s",str);
        int len=strlen(str);
        for(int i=0;i<len;i++) cnt[idx(str[i])]++;
        getpow();
        int ss=cnt[0]*pow[0]+cnt[1]*pow[1]+cnt[2]*pow[2]+cnt[3]*pow[3];
        memset(dp,-1,sizeof(dp[0])*(sz+3));
        return DP(ss);
    }
};
AC ac;
int main()
{
    //freopen("data.in","r",stdin);
    int i,j;
    int n,txt=1;
    while(scanf("%d",&n)!=EOF&&n)
        printf("Case %d: %d\n",txt++,ac.solve(n));
}

【HDU 2296】 Ring

这道题就是一个一般的AC自动机+DP的题,难点就在于要输出最短,且字典序最小的题

加一个ans数组保存一下就行了(我调了一下午难道我会乱说?T_T)

在储存节点的值的时候,开始还想着用结构体来保存,但是后来发现根本没必要啊- =。

不需要区分到底是哪一个串,直接把值加上去就行了_(:з」∠)_

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=1005;
char str[105][20];
int v[105];
struct AC{
    int ch[SIZEN][26],sz;
    int f[SIZEN];
    int val[SIZEN];
    int dp_val[55][SIZEN];
    char stat[SIZEN];
    char ans[55][SIZEN][55];
    void init(){
        memset(ans,0,sizeof(ans));
        memset(dp_val,-1,sizeof(dp_val));
        memset(ch[0],0,sizeof(ch[0]));
        memset(val,0,sizeof(val));
        sz=1;
    }
    int idx(char c){
        return c-'a';
    }
    void insert(char s[],int id){
        int len=strlen(s);
        int rt=0;
        for(int i=0;i<len;i++){
            int t=idx(s[i]);
            if(!ch[rt][t]){
                memset(ch[sz],0,sizeof(ch[sz]));
                stat[sz]=s[i];
                ch[rt][t]=sz++;
            }
            rt=ch[rt][t];
        }
        val[rt]+=v[id];
    }
    void getfail(){
        queue<int> q;
        f[0]=0;
        for(int i=0;i<26;i++){
            int u=ch[0][i];
            if(u) q.push(u);
            f[u]=0;
        }
        while(!q.empty()){
            int r=q.front();q.pop();
            val[r]=val[r]+val[f[r]];
            for(int c=0;c<26;c++){
                int u=ch[r][c];
                if(!u) {ch[r][c]=ch[f[r]][c];continue;}
                f[u]=ch[f[r]][c];
                q.push(u);
            }
        }
    }
    void solve(int n){
        dp_val[0][0]=0;
        for(int i=0;i<n;i++){
            for(int j=0;j<sz;j++)
                if(dp_val[i][j]!=-1){
                for(int k=0;k<26;k++){
                    int ni=i+1;
                    int nj=ch[j][k];
                    int t_val=dp_val[i][j]+val[nj];
                    char t_ans[55];
                    strcpy(t_ans,ans[i][j]);
                    t_ans[i]='a'+k;t_ans[i+1]='\0';
                    if(t_val>dp_val[ni][nj]||(t_val==dp_val[ni][nj]&&strcmp(ans[ni][nj],t_ans)>0)){
                        dp_val[ni][nj]=t_val;
                        strcpy(ans[ni][nj],t_ans);
                    }
                }
            }
        }
        int tans=0;
        char tstat[55];
        for(int i=0;i<=n;i++){
            for(int j=0;j<sz;j++){
                if(dp_val[i][j]==-1) continue;
                if(tans<dp_val[i][j]){
                    strcpy(tstat,ans[i][j]);
                    tans=dp_val[i][j];
                }
                else if(strlen(tstat)<strlen(ans[i][j])) continue;
                else if(tans==dp_val[i][j]&&strcmp(tstat,ans[i][j])>0){
                    tans=dp_val[i][j];
                    strcpy(tstat,ans[i][j]);
                }
            }
        }
        printf("%s\n",tstat);
    }
};
AC ac;
int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int i,j;
    int n,m,_;
    scanf("%d",&_);
    while(_--){
        scanf("%d%d",&n,&m);
        ac.init();
        memset(v,0,sizeof(v));
        for(i=0;i<m;i++){
            scanf("%s",&str[i]);
        }
        for(i=1;i<=m;i++) scanf("%d",&v[i]);v[0]=0;
        for(i=0;i<m;i++) ac.insert(str[i],i+1);
        ac.getfail();
        ac.solve(n);
    }
    return 0;
}

【HDU 2457】DNA repair

这道题就是尽量沿着原串在AC自动机上走去走去的,如果和原串不相同就val+1

DP记录一下就可以过了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define INF 2139062143
using namespace std;
const int SIZEN=1005;
struct AC{
    int ch[SIZEN][4],sz;
    int f[SIZEN],val[SIZEN];
    char ss[SIZEN];
    int dp[SIZEN][SIZEN];
    void init(){
        memset(val,0,sizeof(val));
        memset(ch[0],0,sizeof(ch[0]));
        memset(dp,127,sizeof(dp));
        sz=1;
    }
    int idx(char c){
        if(c=='A') return 0;
        if(c=='G') return 1;
        if(c=='C') return 2;
        if(c=='T') return 3;
    }
    void insert(char s[]){
        int len=strlen(s);
        int rt=0;
        for(int i=0;i<len;i++){
            int t=idx(s[i]);
            if(!ch[rt][t]){
                memset(ch[sz],0,sizeof(ch[sz]));
                ch[rt][t]=sz++;
            }
            rt=ch[rt][t];
        }
        val[rt]++;
    }
    void getfail(){
        queue<int> q;
        f[0]=0;
        for(int c=0;c<4;c++){
            int u=ch[0][c];
            if(u) q.push(u);f[u]=0;
        }
        while(!q.empty()){
            int r=q.front();q.pop();
            if(val[f[r]]) val[r]+=val[f[r]];
            for(int c=0;c<4;c++){
                int u=ch[r][c];
                if(!u) {ch[r][c]=ch[f[r]][c];continue;}
                f[u]=ch[f[r]][c];
                q.push(u);
            }
        }
    }
    int solve(){
        int len=strlen(ss);
        dp[0][0]=0;
        for(int i=0;i<len;i++){
            for(int j=0;j<sz;j++){
                for(int k=0;k<4;k++){
                    if(val[ch[j][k]]) continue;
                    int nj=ch[j][k];
                    int ni=i+1;
                    int t_val=dp[i][j];
                    if(idx(ss[i])!=k) t_val++;
                    dp[ni][nj]=min(dp[ni][nj],t_val);
                }
            }
        }
        int ans=INF;
        for(int i=0;i<sz;i++) ans=min(ans,dp[len][i]);
        if(ans==INF) return -1;
        return ans;
    }
};
AC ac;
int main()
{
    //freopen("data.in","r",stdin);
    int i,j;
    int n,txt=1;
    char str[SIZEN];
    while(scanf("%d",&n)!=EOF&&n){
        ac.init();
        for(i=0;i<n;i++){
            scanf("%s",str);
            ac.insert(str);
        }
        ac.getfail();
        scanf("%s",ac.ss);
        printf("Case %d: %d\n",txt++,ac.solve());
    }
    return 0;
}
【HDU 3247】Resource Archiver
专题里面的压轴题了吧,卡了我好几天,最后还是看题解_(:з」∠),果然还是太弱了

因为直接在AC自动机上跑去跑去的会做大量重复的工作,因此直接预处理出串与串之间的dis然后状压DP就OK了

因为才做了西安邀请赛的重现,写了1010才反应过来这个题该怎么来,感人肺腑啊。。。

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=60005;
const int INF=1000005;
char str[1005];
struct AC{
    int ch[SIZEN][2],sz;
    int val[SIZEN],f[SIZEN];
    int dis[15][15];
    int dp[15][1040],len[20];
    void init(){
        for(int i=0;i<15;i++)
            for(int j=0;j<15;j++)
            dis[i][j]=i==j?0:INF;
        for(int i=0;i<15;i++)
            for(int j=0;j<1040;j++) dp[i][j]=INF;
        memset(val,0,sizeof(val));
        memset(ch[0],0,sizeof(ch[0]));
        sz=1;
    }
    int idx(char c){
        return c-'0';
    }
    int insert(char s[],int v){
        int len=strlen(s);
        int rt=0;
        for(int i=0;i<len;i++){
            int t=idx(s[i]);
            if(!ch[rt][t]){
                memset(ch[sz],0,sizeof(ch[sz]));
                ch[rt][t]=sz++;
            }
            rt=ch[rt][t];
        }
        val[rt]=v;
        return rt;
    }
    void getfail(){
        queue<int> q;
        f[0]=0;
        for(int i=0;i<2;i++){
            int u=ch[0][i];
            if(u) q.push(u);
            f[u]=0;
        }
        while(!q.empty()){
            int r=q.front();q.pop();
            if(val[f[r]]==-1) val[r]=-1;
            for(int i=0;i<2;i++){
                int u=ch[r][i];
                if(!u) {ch[r][i]=ch[f[r]][i];continue;}
                f[u]=ch[f[r]][i];
                q.push(u);
            }
        }
    }
    void query(int s){
        queue<int> q;
        int dd[SIZEN];
        memset(dd,0,sizeof(dd));
        q.push(s);
        int ss=val[s]-1;
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=0;i<2;i++){
                int v=ch[u][i];
                if(dd[v]||v==s) continue;
                if(val[v]==-1) continue;
                dd[v]=dd[u]+1;
                q.push(v);
                if(val[v]) dis[ss][val[v]-1]=dd[v];
            }
        }
    }
    int solve(int n){
        for(int i=0;i<n;i++) dis[i][i]=0;
        for(int i=0;i<n;i++) dp[i][1<<i]=len[i];
        for(int i=1;i<(1<<n);i++){
            for(int j=0;j<n;j++){
                for(int k=0;k<n;k++){
                    int ni=i|(1<<k);
                    dp[k][ni]=min(dp[k][ni],dp[j][i]+dis[j][k]);
                }
            }
        }
        int ans=INF;
        for(int i=0;i<n;i++) ans=min(ans,dp[i][(1<<n)-1]);
        return ans;
    }
    void debug(int n){
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++) printf("%7d ",dis[i][j]);
            printf("\n");
        }
    }
};
AC ac;
int pos[20];
int main()
{
    int i,j;
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0&&m==0) break;
        ac.init();
        for(i=0;i<n;i++){
            scanf("%s",str);
            pos[i]=ac.insert(str,i+1);
            ac.len[i]=strlen(str);
        }
        /*for(i=0;i<ac.sz;i++) printf("%2d ",i);printf("\n");
        for(i=0;i<ac.sz;i++) printf("%2d ",ac.val[i]);printf("\n");
        for(i=0;i<=n;i++) printf("%d ",pos[i]);printf("\n");*/
        for(i=0;i<m;i++){
            scanf("%s",str);
            ac.insert(str,-1);
        }
        ac.getfail();
        for(i=0;i<n;i++) ac.query(pos[i]);
        //ac.debug(n);
        printf("%d\n",ac.solve(n));
    }
    return 0;
}

HDU的题差不多贴完了,吃饭去一会儿再来!

下面是几道其他OJ的题

【ZOJ 3430】Detect the Virus

这道题的难点其实是在于编码的解密吧,然后就是很简单的AC自动机的匹配了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=50500;
char str[SIZEN];
int ret[SIZEN];
bool bit[SIZEN];
struct AC{
    int ch[SIZEN][256],sz;
    int f[SIZEN],last[SIZEN];
    int val[SIZEN];
    bool flag[1000];
    void init(){
        memset(ch[0],0,sizeof(ch[0]));
        memset(last,0,sizeof(last));
        memset(val,0,sizeof(val));
        sz=1;
    }
    void insert(int s[],int v,int len){
        int rt=0;
        for(int i=0;i<len;i++){
            int t=s[i];
            if(!ch[rt][t]){
                memset(ch[sz],0,sizeof(ch[0]));
                ch[rt][t]=sz++;
            }
            rt=ch[rt][t];
        }
        val[rt]=v;
    }
    void getfail(){
        queue<int> q;
        f[0]=0;
        for(int i=0;i<256;i++){
            int u=ch[0][i];
            if(u) q.push(u);
            f[u]=0;
        }
        while(!q.empty()){
            int r=q.front();q.pop();
            for(int c=0;c<256;c++){
                int u=ch[r][c];
                if(!u) {ch[r][c]=ch[f[r]][c];continue;}
                f[u]=ch[f[r]][c];
                q.push(u);
                last[u]=val[f[u]]?f[u]:last[f[u]];
            }
        }
    }
    void add(int j){
        while(j){
            flag[val[j]]=1;
            j=last[j];
        }
    }
    void Find(int s[],int len){
        int j=0;
        for(int i=0;i<len;i++){
            int c=s[i];
            j=ch[j][c];
            if(val[j]) add(j);
                else if(last[j]) add(last[j]);
        }
    }
};
AC ac;
int idx(char c){
    if(c<='Z'&&c>='A') return c-'A';
    if(c<='z'&&c>='a') return c-'a'+26;
    if(c<='9'&&c>='0') return c-'0'+52;
    if(c=='+') return 62;
    if(c=='-') return 63;
}
int trans(){
    int i,j;
    int cnt=0;
    for(i=0;str[i]!='\0'&&str[i]!='=';i++){
        int t=idx(str[i]);
        for(j=5;j>=0;j--) bit[cnt++]=t&(1<<j)?1:0;
    }
    for(;str[i]!='\0';i++) if(str[i]=='=') cnt-=2;
    for(i=0;i<cnt/8;i++){
        ret[i]=0;
        for(j=0;j<8;j++) ret[i]+=bit[i*8+j]<<(7-j);
    }
    return cnt/8;
}
int main()
{
    //freopen("data.in","r",stdin);
    int i,j;
    int n,m;
    while(scanf("%d",&n)!=EOF){
        ac.init();
        for(i=0;i<n;i++){
            scanf("%s",str);
            int len=trans();
            ac.insert(ret,i+1,len);
        }
        ac.getfail();
        scanf("%d",&m);
        for(i=0;i<m;i++){
            scanf("%s",str);
            memset(ac.flag,0,sizeof(ac.flag));
            int len=trans();
            ac.Find(ret,len);
            int ans=0;
            for(j=1;j<=n;j++) ans+=ac.flag[j];
            printf("%d\n",ans);
        }
        printf("\n");
    }
    return 0;
}
【ZOJ 3228】Searching the String

这道题的主要是要记录该串在上一次遇到的位置,然后就可以确定是否重叠了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=100005*6;
char str[SIZEN],ss[20];
struct AC{
    int ch[SIZEN][26],sz;
    int f[SIZEN],ll[SIZEN];
    int cnt[2][SIZEN],val[SIZEN];
    int dep[SIZEN],last[SIZEN];
    void init(){
        memset(last,0,sizeof(last));
        memset(val,0,sizeof(val));
        memset(ch[0],0,sizeof(ch[0]));
        memset(cnt,0,sizeof(cnt));
        memset(ll,-1,sizeof(ll));
        sz=1;dep[0]=0;
    }
    int idx(char c){
        return c-'a';
    }
    int insert(char s[]){
        int len=strlen(s);
        int rt=0;
        for(int i=0;i<len;i++){
            int t=idx(s[i]);
            if(!ch[rt][t]){
                memset(ch[sz],0,sizeof(ch[0]));
                dep[sz]=dep[rt]+1;
                ch[rt][t]=sz++;
            }
            rt=ch[rt][t];
        }
        val[rt]=1;
        return rt;
    }
    void getfail(){
        queue<int> q;
        f[0]=0;
        for(int i=0;i<26;i++){
            int u=ch[0][i];
            if(u) q.push(u);
            f[u]=0;
        }
        while(!q.empty()){
            int r=q.front();q.pop();
            for(int i=0;i<26;i++){
                int u=ch[r][i];
                if(!u) {ch[r][i]=ch[f[r]][i];continue;}
                f[u]=ch[f[r]][i];
                q.push(u);
                last[u]=val[f[u]]?f[u]:last[f[u]];
            }
        }
    }
    void add(int j,int loc){
        while(j){
            cnt[0][j]++;
            if(loc-ll[j]>=dep[j]){
                cnt[1][j]++;
                ll[j]=loc;
            }
            j=last[j];
        }
    }
    void find(char s[]){
        int len=strlen(s);
        int j=0;
        for(int i=0;i<len;i++){
            int c=idx(s[i]);
            j=ch[j][c];
            add(j,i);
        }
    }
};
AC ac;
int pos[SIZEN],type[SIZEN];
int txt=1;
int main()
{
    //freopen("data.in","r",stdin);
    int i,j;
    int n;
    while(scanf("%s",str)!=EOF){
        scanf("%d",&n);
        ac.init();
        for(i=0;i<n;i++){
            scanf("%d %s",&type[i],ss);
            pos[i]=ac.insert(ss);
        }
        ac.getfail();
        ac.find(str);
        printf("Case %d\n",txt++);
        for(i=0;i<n;i++) printf("%d\n",ac.cnt[type[i]][pos[i]]);
        printf("\n");
    }
    return 0;
}

【POJ 2778】DNA Sequence

这道题是很基础的在AC自动机上面跑DP的题了

也是需要用到矩阵,是HDU 2243的简化版吧

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define mod 100000
using namespace std;
typedef long long LL;
const int SIZEN=110;
char str[100];
struct Mat{
    LL mat[SIZEN][SIZEN];
    void init(){
        memset(mat,0,sizeof(mat));
    }
    void getE(){
        memset(mat,0,sizeof(mat));
        for(int i=0;i<SIZEN;i++) mat[i][i]=1;
    }
    void output(int sz){
        for(int i=0;i<sz;i++){
            for(int j=0;j<sz;j++) printf("%I64d ",mat[i][j]);
            printf("\n");
        }
    }
};
struct AC{
    int ch[SIZEN][4],sz;
    int f[SIZEN],val[SIZEN];
    int idx(char c){
        if(c=='A') return 0;
        if(c=='G') return 1;
        if(c=='C') return 2;
        if(c=='T') return 3;
    }
    void init(){
        memset(ch[0],0,sizeof(ch[0]));
        memset(val,0,sizeof(val));
        sz=1;
    }
    void insert(char s[]){
        int len=strlen(s);
        int rt=0;
        for(int i=0;i<len;i++){
            int t=idx(str[i]);
            if(!ch[rt][t]){
                memset(ch[sz],0,sizeof(ch[0]));
                ch[rt][t]=sz++;
            }
            rt=ch[rt][t];
        }
        val[rt]=1;
    }
    void getfail(){
        queue<int> q;
        f[0]=0;
        for(int i=0;i<4;i++){
            int u=ch[0][i];
            if(u) {q.push(u);f[u]=0;}
        }
        while(!q.empty()){
            int r=q.front();q.pop();
            if(val[f[r]]) val[r]=1;
            for(int i=0;i<4;i++){
                int u=ch[r][i];
                if(!u) {ch[r][i]=ch[f[r]][i];continue;}
                f[u]=ch[f[r]][i];
                q.push(u);
            }
        }
    }
};
AC ac;
Mat A;
Mat operator *(Mat a,Mat b){
    Mat ret;
    ret.init();
    for(int i=0;i<=ac.sz;i++)
    for(int j=0;j<=ac.sz;j++)
    for(int k=0;k<=ac.sz;k++){
        ret.mat[i][j]=(ret.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod;
    }
    return ret;
}
Mat operator ^(Mat a,int p){
    Mat ret;
    ret.getE();
    while(p){
        if(p&1) ret=ret*a;
        a=a*a;
        p>>=1;
    }
    return ret;
}
LL pow(int b){
    LL ret=1,a=4;
    while(b){
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}
int main()
{
    int i,j;
    int n,m;
    while(scanf("%d%d",&m,&n)!=EOF){
        ac.init();
        for(i=0;i<m;i++){
            scanf("%s",str);
            ac.insert(str);
        }
        ac.getfail();
        A.init();
        for(i=0;i<ac.sz;i++){
            if(ac.val[i]) continue;
            for(j=0;j<4;j++){
                int nj=ac.ch[i][j];
                if(ac.val[nj]) A.mat[i][ac.sz]++;
                    else A.mat[i][nj]++;
            }
        }
        A.mat[ac.sz][ac.sz]=4;
        //A.output(ac.sz+2);
        Mat ret=A^n;
        //ret.output(ac.sz+2);
        LL ans=(pow(n)-ret.mat[0][ac.sz]+mod)%mod;
        printf("%I64d\n",ans);
    }
    return 0;
}

【POJ 1625】Censored!

 这道题在想法上没什么多说的,主要是练习了一下高精度的使用(竟然被高精度坑了有1个多小时_(:з」∠)_)

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define mod 10000000
using namespace std;
const int SIZEN=155;
char str[SIZEN];
struct Big{
    int num[205];
    void init(){
        memset(num,0,sizeof(num));
    }
    void output(){
        int j=204;
        while(!num[j]&&j>0) j--;
        printf("%d",num[j--]);
        while(j>=0) printf("%07d",num[j--]);
        printf("\n");
    }
};
Big operator + (Big a,Big b){
    Big ret;
    ret.init();
    for(int i=0;i<SIZEN;i++)
        ret.num[i]=a.num[i]+b.num[i];
    for(int i=0;i<SIZEN-1;i++){
        ret.num[i+1]+=ret.num[i]/mod;
        ret.num[i]%=mod;
    }
    return ret;
}
struct AC{
    char ss[SIZEN];
    int ch[105][51],sz;
    int f[105],val[105];
    int map[515];
    Big dp[51][105];
    void init(){
        memset(map,-1,sizeof(map));
        memset(val,0,sizeof(val));
        memset(ch[0],0,sizeof(ch[0]));
        for(int i=0;i<51;i++)
        for(int j=0;j<105;j++) dp[i][j].init();
        sz=1;
    }
    void getmap(){
        gets(ss);
        int len=strlen(ss);
        for(int i=0;i<len;i++)
            map[ss[i]+128]=i;
    }
    int idx(char c){
        return map[c+128];
    }
    void insert(char s[],int v){
        int len=strlen(s);
        int rt=0;
        for(int i=0;i<len;i++){
            int t=idx(s[i]);
            if(!ch[rt][t]){
                memset(ch[sz],0,sizeof(ch[0]));
                ch[rt][t]=sz++;
            }
            rt=ch[rt][t];
        }
        val[rt]=v;
    }
    void getfail(int n){
        queue<int> q;
        f[0]=0;
        for(int i=0;i<n;i++){
            int u=ch[0][i];
            if(u) q.push(u);f[u]=0;
        }
        while(!q.empty()){
            int r=q.front();q.pop();
            if(val[f[r]]) val[r]=val[f[r]];
            for(int i=0;i<n;i++){
                int u=ch[r][i];
                if(!u) {ch[r][i]=ch[f[r]][i];continue;}
                f[u]=ch[f[r]][i];
                q.push(u);
            }
        }
    }
    void solve(int n,int m){
        dp[0][0].num[0]=1;
        for(int i=0;i<m;i++){
            int ni=i+1;
            for(int j=0;j<sz;j++)
                if(val[j]==0){
                    for(int k=0;k<n;k++){
                        int nj=ch[j][k];
                        if(val[nj]) continue;
                        dp[ni][nj]=dp[ni][nj]+dp[i][j];
                    }
                }
        }
    }
};
AC ac;
int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int i,j;
    int n,m,p;
    while(scanf("%d%d%d",&n,&m,&p)!=EOF){
        ac.init();
        getchar();
        ac.getmap();
        for(i=0;i<p;i++){
            gets(str);
            ac.insert(str,1);
        }
        ac.getfail(n);
        ac.solve(n,m);
        Big ans;
        ans.init();
        for(i=0;i<ac.sz;i++) ans=ans+ac.dp[m][i];
        ans.output();
    }
    return 0;
}

好了差不多就只能刷这么多题了T_T

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值