ACM 字符串 题目整理

AC自动机

UVa 11468  Substring

AC自动机+概率DP。

注意要补全不存在的边。

为什么要补全不存在的边呢?补全以后可以直接找到状态的转移,即从所有子节点就可以实现所有状态转移。

#include<iostream>
#include<vector>
#include<cmath>
#include<map>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<string>
#define INF 1000000000LL
#define ll long long
#define maxnode 4000+5
#define sigma_size 62
using namespace std;
struct Tire
{
    int ch[maxnode][sigma_size+5],end[maxnode],fail[maxnode];
    int root,L;
    int newnode()
    {
        memset(ch[L],0,sizeof(ch[L]));
        end[L++]=0;
        return L-1;
    }
    void init()
    {
        L=0;
        fail[0]=0;
        end[0]=0;
        root=newnode();
    }
    int idx(char c)
    {
        if('a'<=c&&c<='z') return c-'a';
        else if('A'<=c&&c<='Z') return c-'A'+26;
        else if('0'<=c&&c<='9') return c-'0'+52;
    }
    void insert(char* word)
    {
        int now=0;
        for(int i=0; word[i]; ++i)
        {
            int x=idx(word[i]);
            if(!ch[now][x])
                ch[now][x]=newnode();
            now=ch[now][x];
        }
        end[now]=1;
    }
    void build()
    {
        queue<int> que;
        for(int i=0; i<sigma_size; ++i)
        {
            if(ch[0][i])
            {
                que.push(ch[0][i]);
                fail[ch[0][i]]=0;
            }
        }
        while(!que.empty())
        {
            int q=que.front();
            que.pop();
            for(int i=0; i<sigma_size; ++i)
            {
                int u=ch[q][i];
                if(!u)
                {
                    ch[q][i]=ch[fail[q]][i];
                    continue;
                }
                int v=fail[q];
                que.push(u);
                while(v&&!ch[v][i]) v=fail[v];
                fail[u]=ch[v][i];
                end[u]|=end[fail[u]];
            }
        }
    }
};
Tire ac;
double pro[sigma_size+5];
double f[maxnode+5][105];
bool vis[maxnode+5][105];
bool cha[sigma_size+5];
double dp(int now,int L)
{
    if(!L) return 1.0;
    if(vis[now][L]) return f[now][L];
    vis[now][L]=true;
    f[now][L]=0.0;
    for(int i=0; i<sigma_size; ++i)
        if(cha[i])
        {
            int u=ac.ch[now][i];
            if(!ac.end[u])
                f[now][L]+=pro[i]*dp(u,L-1);
        }
    return f[now][L];
}
int main()
{
    int T,kase=0;
    //freopen("read.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    scanf("%d",&T);
    while(T--)
    {
        int k;
        scanf("%d",&k);
        ac.init();
        memset(vis,0,sizeof(vis));
        memset(cha,0,sizeof(cha));
        for(int i=0; i<k; ++i)
        {
            char word[100];
            scanf("%s",word);
            ac.insert(word);
        }
        ac.build();
        int n;
        scanf("%d",&n);
        memset(pro,0,sizeof(pro));
        for(int i=0; i<n; ++i)
        {
            char word[3];
            double p;
            scanf("%s%lf",word,&p);
            int q=ac.idx(word[0]);
            cha[q]=true;
            pro[q]=p;
        }
        int L;
        scanf("%d",&L);
        double ans=dp(0,L);
        printf("Case #%d: %.6lf\n",++kase,ans);
    }
    return 0;
}
View Code

 

HDU 2825 Wireless Password

AC自动机+状态压缩DP

求长度为n的字符串中包含超过k个模式串的个数。

dp[i][j][k]表示长为i,当前状态为j时的字符串包括k个串时的个数,k为集合。

状态压缩使每个串唯一,而不必考虑重复。串可以重叠而考虑AC自动机。

另外注意如果当前dp为0,那就不用刷表了。

#include<iostream>
#include<vector>
#include<cmath>
#include<map>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<string>
#define ll long long
#define maxnode 105
#define sigma_size 26
#define maxn 100
#define MOD 20090717
using namespace std;
int n,m,K;
int countBit(int val)
{
    int cnt=0;
    while(val)
    {
        cnt+=val%2;
        val/=2;
    }
    return cnt;
}
struct Tire
{
    int ch[maxnode][sigma_size],end[maxnode],fail[maxnode];
    int root,L;
    int newnode()
    {
        memset(ch[L],0,sizeof(ch[L]));
        end[L++]=0;
        return L-1;
    }
    void init()
    {
        L=0;
        fail[0]=0;
        end[0]=0;
        root=newnode();
    }
    int idx(char c)
    {
        return c-'a';
    }
    void insert(char* word,int p)
    {
        int now=0;
        for(int i=0; word[i]; ++i)
        {
            int x=idx(word[i]);
            if(!ch[now][x])
                ch[now][x]=newnode();
            now=ch[now][x];
        }
        end[now]=1<<p;
    }
    void build()
    {
        queue<int> que;
        for(int i=0; i<sigma_size; ++i)
        {
            if(ch[0][i])
            {
                que.push(ch[0][i]);
                fail[ch[0][i]]=0;
            }
        }
        while(!que.empty())
        {
            int q=que.front();
            que.pop();
            for(int i=0; i<sigma_size; ++i)
            {
                int u=ch[q][i];
                if(!u)
                {
                    ch[q][i]=ch[fail[q]][i];
                    continue;
                }
                int v=fail[q];
                que.push(u);
                while(v&&!ch[v][i]) v=fail[v];
                fail[u]=ch[v][i];
                end[u]|=end[fail[u]];
            }
        }
    }
    int dp[27][maxn][(1<<10)+1];
    int solve()
    {
        memset(dp,0,sizeof(dp));
        dp[0][0][0]=1;
        for(int i=0; i<n; ++i)
        {
            for(int j=0; j<L; ++j)
            {
                for(int k=0; k<(1<<m); ++k)
                    if(dp[i][j][k])
                    {
                        for(int l=0; l<26; ++l)
                        {
                            int u=ch[j][l];
                            int &res=dp[i+1][u][k|end[u]];
                            res+=dp[i][j][k];
                            res%=MOD;
                        }
                    }
            }
        }
        int ans=0;
        for(int j=0; j<(1<<m); ++j)
            if(countBit(j)>=K)
            {
                for(int i=0; i<L; ++i)
                {
                    ans+=dp[n][i][j];
                    ans%=MOD;
                }
            }
        return ans;
    }
};
Tire ac;
int main()
{
    while(scanf("%d%d%d",&n,&m,&K)!=EOF)
    {
        if(!n&&!m&&!K) break;
        ac.init();
        for(int i=0; i<m; ++i)
        {
            char word[30];
            scanf("%s",word);
            ac.insert(word,i);
        }
        ac.build();
        printf("%d\n",ac.solve());
    }
    return 0;
}
View Code

 

POJ 3691 DNA repair

AC自动机+DP

问修改最少字符使得串中不含模式子串。

dp[i][j]表示当前是当串中第j个位置是第i个节点状态时的最优解。

当c不是结束节点的时候,c表示从i转移到的状态

dp[i][j]=min{dp[c][j+1]+1}//如果此时需要修改

dp[i][j]=min{dp[c][j+1]}//如果此时不修改

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#define ll long long
using namespace std;
const int maxnode =1005;
const int sigma_size=4;
const int inf =1000000000;
struct Tire
{
    int ch[maxnode][sigma_size];
    int end[maxnode],fail[maxnode];
    int sz;
    int newnode()
    {
        memset(ch[sz],0,sizeof(ch[sz]));
        fail[sz]=0;
        end[sz++]=0;
        return sz-1;
    }
    void init()
    {
        sz=0;
        newnode();
    }
    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 *word)
    {
        int now=0;
        for(int i=0; word[i]; ++i)
        {
            int x=idx(word[i]);
            if(!ch[now][x])
                ch[now][x]=newnode();
            now=ch[now][x];
        }
        end[now]=1;
    }
    void build()
    {
        queue<int> que;
        for(int i=0; i<sigma_size; ++i)
        {
            int u=ch[0][i];
            if(u)
            {
                fail[u]=0;
                que.push(u);
            }
        }
        while(!que.empty())
        {
            int q=que.front();
            que.pop();
            for(int i=0; i<sigma_size; ++i)
            {
                int u=ch[q][i],v=fail[q];
                if(!u)
                {
                    ch[q][i]=ch[v][i];
                    continue;
                }
                que.push(u);
                while(v&&!ch[v][i]) v=fail[v];
                fail[u]=ch[v][i];
                end[u]|=end[fail[u]];
            }
        }
    }
};
bool vis[1005][1005];
int f[1005][1005];
char str[1005];
int N;
Tire ac;
int dp(int now,int L)
{
    if(L==N) return 0;
    if(vis[now][L]) return f[now][L];
    vis[now][L]=true;
    f[now][L]=inf;
    for(int i=0; i<4; ++i)
    {
        int y=ac.idx(str[L]);
        int u=ac.ch[now][i];
        if(!ac.end[u])
        {
            if(y!=i&&dp(u,L+1)!=inf)
                f[now][L]=min(f[now][L],dp(u,L+1)+1);
            else if(y==i&&dp(u,L+1)!=inf)
                f[now][L]=min(f[now][L],dp(u,L+1));
        }
    }
    return f[now][L];
}
int main()
{
    int n,kase=0;
    while(scanf("%d",&n)&&n)
    {
        char word[30];
        ac.init();
        memset(vis,0,sizeof(vis));
        for(int i=1; i<=n; ++i)
        {
            scanf("%s",word);
            ac.insert(word);
        }
        scanf("%s",str);
        ac.build();
        N=strlen(str);

        printf("Case %d: ",++kase);
        int ans=dp(0,0);
        if(ans==inf) puts("-1");
        else printf("%d\n",ans);
    }
    return 0;
}
View Code

 

 

字典树

POJ 3630 Phone List

问有没有一个电话号是另一个电话号的前缀。

典型的trie树,时间复杂度是10n。注意不要边读入边判断。否则像12、2这样就判不出来。

#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#define LL unsigned int
using namespace std;
const int maxnode =1000005;
const int sigma_size=12;
struct Tire
{
    int ch[maxnode][sigma_size];
    int end[maxnode];
    int sz;
    int newnode()
    {
        memset(ch[sz],0,sizeof(ch[sz]));
        end[sz]=0;
        return sz++;
    }
    void init()
    {
        sz=0;
        newnode();
    }
    bool insert(char *word,int v)
    {
        int now=0;
        for(int i=0; word[i]; ++i)
        {
            int x=word[i]-'0';
            if(!ch[now][x])
                ch[now][x]=newnode();
            now=ch[now][x];
            if(end[now]&&end[now]!=v)
                return false;
        }
        end[now]=v;
        return true;
    }
};
Tire tree;
char numb[100005][12];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        bool ok=true;
        tree.init();
        for(int i=1; i<=n; ++i)
        {
            scanf("%s",numb[i]);
            if(ok&&!tree.insert(numb[i],i))
                ok=false;
        }
        for(int i=1; i<=n&&ok; ++i)
        {
            if(!tree.insert(numb[i],i))
                ok=false;
        }
        if(ok) puts("YES");
        else puts("NO");
    }
    return 0;
}
View Code

 

POJ 1204 Word Puzzles

在一个字符矩阵中寻找出现的字符串,并输出头字符的位置和方向。

可以用字典树来做。枚举每个位置再枚举方向,这样每次遍历一个字符串看是否出现了模式串。

#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#define LL unsigned int
using namespace std;
const int maxnode =1000*1000+5;
const int sigma_size=26;
char mat[1005][1005];
int C,L,W;
bool judge(int x,int y)
{
    return 0<=x&&x<L&&0<=y&&y<C;
}
int ax,ay,d;
int ans[1005][3];
const int dir[8][2]= {{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
struct Tire
{
    int ch[maxnode][sigma_size];
    int end[maxnode];
    int sz;
    int newnode()
    {
        memset(ch[sz],-1,sizeof(ch[sz]));
        end[sz]=0;
        return sz++;
    }
    void init()
    {
        sz=0;
        newnode();
    }
    int idx(char c)
    {
        return c-'A';
    }
    void insert(char *word,int v)
    {
        int now=0;
        for(int i=0; word[i]; ++i)
        {
            int x=idx(word[i]);
            if(ch[now][x]==-1)
                ch[now][x]=newnode();
            now=ch[now][x];
        }
        end[now]=v;
    }
    void find(int x,int y)
    {
        int now=0;
        while(1)
        {
            int c=idx(mat[x][y]);
            now=ch[now][c];
            if(now==-1) break;
            if(end[now])
            {
                ans[end[now]][0]=ax;
                ans[end[now]][1]=ay;
                ans[end[now]][2]=d;
                end[now]=0;
            }
            x+=dir[d][0];
            y+=dir[d][1];
            if(!judge(x,y)) break;
        }
    }
    void find(int x,int y,int now)
    {
        if(now==-1 )return;
        if(end[now]>0)
        {
            ans[end[now]][0]=ax;
            ans[end[now]][1]=ay;
            ans[end[now]][2]=d;
            end[now]=0;
        }
        if(!judge(x,y)) return;
        find(x+dir[d][0],y+dir[d][1],ch[now][idx(mat[x][y])]);
    }
};
Tire tree;
int main()
{
    while(scanf("%d%d%d",&L,&C,&W)!=EOF)
    {
        for(int i=0; i<L; ++i)
            scanf("%s",mat[i]);
        tree.init();
        for(int i=1; i<=W; ++i)
        {
            char str[1005];
            scanf("%s",str);
            tree.insert(str,i);
        }
        for(int i=0; i<L; ++i)
            for(int j=0; j<C; ++j)
                for(int k=0; k<8; ++k)
                {
                    ax=i;
                    ay=j;
                    d=k;
                    tree.find(i,j);
                }
        for(int i=1; i<=W; ++i)
            printf("%d %d %c\n",ans[i][0],ans[i][1],ans[i][2]+'A');
    }
    return 0;
}
View Code

 

你可能感兴趣的:(ACM 字符串 题目整理)