寒假实录 chapter 1

P1006 传纸条

解:题意可以转化为两个人从起点出发到终点的不重复路径,定义数组dp[i][j][k],表示前i步第一个人在j列,第二个人在k列的最大满意度。

#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
int a[100][100];
int dp[200][100][100];
int n,m;
int main()
{
    scanf("%d %d",&m,&n);
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&a[i][j]);
        }
    }
    memset(dp,-1,sizeof dp);
    dp[2][1][1]=0;
    for(int i=3;i<n+m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            for(int k=j+1;k<=n;k++)
            {
                int tmp=dp[i][j][k];
                if(dp[i-1][j][k]>tmp) tmp=dp[i-1][j][k];
                if(dp[i-1][j-1][k]>tmp) tmp=dp[i-1][j-1][k];
                if(dp[i-1][j][k-1]>tmp) tmp=dp[i-1][j][k-1];
                if(dp[i-1][j-1][k-1]>tmp) tmp=dp[i-1][j-1][k-1];
                if(tmp==-1) continue;
                dp[i][j][k]=tmp+a[i-j][j]+a[i-k][k];
            }
        }
    }
    printf("%d\n",max(dp[n+m-1][n][n-1],dp[n+m-1][n-1][n]));
    return 0;

}

P1417 烹调方案

解:乍一看我觉得事背包问题,其实b[i]这个属性对于顺序是有影响的。

两种情况:当i先被选时有x=a_{i}-(t+c_{i})*b_{i}+a_{j}-(t+c_{i}+c_{j})*b_{j}

当j先被选时有y=a_{j}-(t+c_{j})*b_{j}+a_{i}-(t+c_{i}+c_{j})*b_{i}

当i应当被选时有x>y,化简后即为-b_{j}*c_{i}>-b_{i}*c_{j}

按照这个结果排个序。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
struct node
{
    ll a,b,c;
}p[100];
int T,n;
ll dp[100010];
bool cmp(node x,node y)
{
    return -y.b*x.c>-x.b*y.c;
}
int main()
{
    scanf("%d %d",&T,&n);
    for(int i=1;i<=n;i++) scanf("%d",&p[i].a);
    for(int i=1;i<=n;i++) scanf("%d",&p[i].b);
    for(int i=1;i<=n;i++) scanf("%d",&p[i].c);
    sort(p+1,p+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        for(int j=T;j>=p[i].c;j--)
        {
            dp[j]=max(dp[j-p[i].c]+p[i].a-j*p[i].b,dp[j]);
        }
    }
    ll ans=0;
    for(int i=1;i<=T;i++) ans=max(ans,dp[i]);
    printf("%d\n",ans);
    return 0;
}

P1736 创意吃鱼法

解:用两个数组a,b分别维护向左(右)和向上的最长的0(不包括当前点)。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int dp[2510][2510];
int n,m;
int a[2510][2510];
int L[2510][2510],up[2510][2510];
int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        memset(L,0,sizeof L);
        memset(up,0,sizeof up);
        memset(dp,0,sizeof dp);
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(!a[i][j])
                {
                    L[i][j]=L[i][j-1]+1;
                    up[i][j]=up[i-1][j]+1;
                }
                else
                {
                    dp[i][j]=min(dp[i-1][j-1],min(L[i][j-1],up[i-1][j]))+1;
                    ans=max(ans,dp[i][j]);
                }
            }
        }
        memset(L,0,sizeof L);
        memset(up,0,sizeof up);
        memset(dp,0,sizeof dp);
        for(int i=1;i<=n;i++)
        {
            for(int j=m;j>=1;j--)
            {
                if(!a[i][j])
                {
                    L[i][j]=L[i][j+1]+1;
                    up[i][j]=up[i-1][j]+1;
                }
                else
                {
                    dp[i][j]=min(dp[i-1][j+1],min(L[i][j+1],up[i-1][j]))+1;
                    ans=max(ans,dp[i][j]);
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

P1064 金明的预算方案

解:有依赖的背包问题。因为本题的主件最多只有2个附件。那么这个主机的所有附件组合集合可以直接枚举。转化为分组背包问题,每组的物品即是该主件和附件的所有组合情况。

小数据写法

#include <bits/stdc++.h>
using namespace std;
#define ll long long
struct node
{
    int v,p,q;
};
node a[100];
vector<node> g[100];
vector<node> p[100];
int dp[32010];
int N,m;
int main()
{
    scanf("%d %d",&N,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d %d",&a[i].v,&a[i].p,&a[i].q);
        if(a[i].q)
        {
            g[a[i].q].push_back(a[i]);
        }
        else g[i].push_back(a[i]);
    }
    for(int i=1;i<=m;i++)
    {
        if(g[i].size()==3)
        {
            p[i].push_back(node{g[i][0].v,g[i][0].v*g[i][0].p,0});
            p[i].push_back(node{g[i][0].v+g[i][1].v,g[i][0].v*g[i][0].p+g[i][1].v*g[i][1].p,0});
            p[i].push_back(node{g[i][0].v+g[i][2].v,g[i][0].v*g[i][0].p+g[i][2].v*g[i][2].p,0});
            p[i].push_back(node{g[i][0].v+g[i][2].v+g[i][1].v,g[i][0].v*g[i][0].p+g[i][1].v*g[i][1].p+g[i][2].v*g[i][2].p,0});
        }
        else if(g[i].size()==2)
        {
            p[i].push_back(node{g[i][0].v,g[i][0].v*g[i][0].p,0});
            p[i].push_back(node{g[i][0].v+g[i][1].v,g[i][0].v*g[i][0].p+g[i][1].v*g[i][1].p,0});
        }
        else if(g[i].size()==1)
        {
            p[i].push_back(node{g[i][0].v,g[i][0].v*g[i][0].p,0});
        }
    }
    for(int i=1;i<=m;i++)
    {
        if(p[i].size()!=0)
        {
            for(int j=N;j>=0;j--)
            {
                for(int k=0;k<p[i].size();k++)
                {
                    if(j>=p[i][k].v)
                        dp[j]=max(dp[j],dp[j-p[i][k].v]+p[i][k].p);
                }
            }
        }
    }
    printf("%d\n",dp[N]);
    return 0;
}

大数据写法(貌似我的常熟不够优秀)(做法参见背包九讲2.0)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
struct node
{
    int v,p,q;
};
node a[1010];
vector<node> g[1010];
vector<node> p[1010];
int dp[100010];
int N,m;
int main()
{
    scanf("%d %d",&N,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d %d",&a[i].v,&a[i].p,&a[i].q);
        if(a[i].q)
        {
            g[a[i].q].push_back(a[i]);
        }
        else g[i].push_back(a[i]);
    }
    for(int i=1;i<=m;i++)
    {
        if(g[i].size()!=0)
        {
            memset(dp,-1,sizeof dp);
            dp[0]=0;
            for(int j=1;j<(int)g[i].size();j++){
                for(int k=N-g[i][0].v;k>=g[i][j].v;k--)
                {
                    if(dp[k]<dp[k-g[i][j].v]+g[i][j].v*g[i][j].p&&dp[k-g[i][j].v]!=-1)
                        dp[k]=dp[k-g[i][j].v]+g[i][j].v*g[i][j].p;
                }
            }
            for(int j=0;j<=N-g[i][0].v;j++)
            {
                if(dp[j]!=-1)
                {
                    p[i].push_back(node{j+g[i][0].v,dp[j]+g[i][0].v*g[i][0].p,0});
                }
            }
        }
    }
    memset(dp,0,sizeof dp);
    for(int i=1;i<=m;i++)
    {
        if(p[i].size()!=0)
        {
            for(int j=N;j>=0;j--)
            {
                for(int k=0;k<p[i].size();k++)
                {
                    if(j>=p[i][k].v)
                        dp[j]=max(dp[j],dp[j-p[i][k].v]+p[i][k].p);
                }
            }
        }
    }
    printf("%d\n",dp[N]);
    return 0;
}

P1541 乌龟棋

定义dp[i][j][k][h],表示当选i张为1的牌、j张为2的牌、k张为3的牌和h张为4的牌的最大分数。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int N,M;
int a[400];
int b[10];
int dp[42][42][42][42];
int main()
{
    scanf("%d %d",&N,&M);
    for(int i=1;i<=N;i++) scanf("%d",&a[i]);
    for(int j=1;j<=M;j++)
    {
        int x;
        scanf("%d",&x);
        b[x]++;
    }
    dp[0][0][0][0]=a[1];
    for(int i=0;i<=b[1];i++)
    {
        for(int j=0;j<=b[2];j++)
        {
            for(int k=0;k<=b[3];k++)
            {
                for(int l=0;l<=b[4];l++)
                {
                    int now=1+i+2*j+3*k+4*l;
                    if(i!=0)
                        dp[i][j][k][l]=max(dp[i][j][k][l],dp[i-1][j][k][l]+a[now]);
                    if(j!=0)
                        dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j-1][k][l]+a[now]);
                    if(k!=0)
                        dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k-1][l]+a[now]);
                    if(l!=0)
                        dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k][l-1]+a[now]);
                }
            }
        }
    }
    printf("%d\n",dp[b[1]][b[2]][b[3]][b[4]]);
    return 0;
}

问题记录:为何不能定义dp[i][j]表示用i张牌走到下标为j的时候的最大分数?

P1063 能量项链

解:发现转移方程形如dp[i][j]\leftarrow (dp[i][k],dp[k][j])大概就是区间dp了吧,本题定义dp[i][j]表示数串以a[i]开头,a[j]结尾的最大能量。因为本题是环形,只需要将数组a在复制一段,破环为链。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int N;
ll a[400];
ll dp[410][410];
int main()
{
    scanf("%d",&N);
    for(int i=1;i<=N;i++)
    {
        scanf("%lld",&a[i]);
        a[i+N]=a[i];
    }
    for(int i=2;i<=N;i++)
    {
        for(int j=1;j+i<=2*N;j++)
        {
            int k=j+i;
            for(int l=j+1;l<=k-1;l++)
            {
                dp[j][k]=max(dp[j][k],dp[j][l]+dp[l][k]+a[j]*a[k]*a[l]);
            }
        }
    }
    ll ans=0;
    for(int i=1;i<=N;i++) ans=max(ans,dp[i][i+N]);
    printf("%lld\n",ans);
    return 0;
}

P1052 过河

解:耳目一新,缩点。因为LCM(1...10)=2520,即无论跳多远和怎么跳都会到达2520这个点,即石头之间的距离对2520取模即可将距离等效缩小。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int L;
int s,t,M;
int dp[300000];
ll a[200];
int stone[300000];
int flag[200];
int d[200];
int main()
{
    scanf("%d",&L);
    scanf("%d %d %d",&s,&t,&M);
    for(int i=1;i<=M;i++) scanf("%lld",&a[i]);
    sort(a+1,a+M+1);
    for(int i=1;i<=M;i++)
    {
        flag[i]=(a[i]-a[i-1])%2520;
    }
    for(int i=1;i<=M;i++)
    {
        d[i]=flag[i]+d[i-1];
        stone[d[i]]++;
    }
    L=d[M]+t;
    for(int i=1;i<=L;i++) dp[i]=M;
    dp[0]=0;
    for(int i=1;i<=L;i++)
    {
        for(int j=s;j<=t;j++)
        {
            if(j>i) continue;
            if(stone[i]) dp[i]=min(dp[i],dp[i-j]+stone[i]);
            else dp[i]=min(dp[i],dp[i-j]);
        }
    }
    int ans=M;
    for(int i=d[M];i<d[M]+t;i++) ans=min(ans,dp[i]);
    printf("%d\n",ans);
    return 0;
}

Blue Jeans

解:求多个串的最长公共子串。枚举第一个字符串的所有子串用KMP与其他的串进行匹配。

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
string s[20];
int nexts[100];
vector<string> ans;
void getnext(string x)
{
    int m=x.size();
    int i,j;
    j=nexts[0]=-1;
    i=0;
    while(i<m)
    {
        while(j!=-1&&x[i]!=x[j]) j=nexts[j];
        nexts[++i]=++j;
    }
}
bool kmp(string x,string y)
{
    int n=y.size();
    int m=x.size();
    getnext(x);
    int i,j;i=j=0;
    while(i<n)
    {
        while(j!=-1&&x[j]!=y[i]) j=nexts[j];
        ++j;++i;
        if(j>=m)
        {
            return true;
        }
    }
    return false;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin>>T;
    while(T--)
    {
        int M;
        cin>>M;
        for(int i=1;i<=M;i++)
        {
            cin>>s[i];
        }
        string com;
        ans.clear();
        for(int i=0;i<s[1].size();i++)
        {
            for(int j=i;j<s[1].size();j++)
            {
                com.clear();
                for(int k=i;k<=j;k++) com+=s[1][k];
                if((int)com.size()<3) continue;
                int flag=0;
                for(int k=2;k<=M;k++)
                {
                    if(kmp(com,s[k])==true)
                    {
                        flag++;
                    }
                }
                if(flag==M-1) ans.push_back(com);
            }
        }
        int mx=0;
        for(int i=0;i<(int)ans.size();i++) mx=max(mx,(int)ans[i].size());
        if(mx<3) cout<<"no significant commonalities"<<'\n';
        else
        {
            sort(ans.begin(),ans.end());
            for(int i=0;i<(int)ans.size();i++)
            {
                if((int)ans[i].size()==mx)
                {
                    cout<<ans[i]<<'\n';
                    break;
                }
            }
        }
    }
    return 0;
}

Count the string

解:KMP next数组的理解题。

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
#define mod 10007
char s[200010];
int nexts[200010];
void getnext(char x[])
{
    int m=strlen(x);
    int i,j;
    j=nexts[0]=-1;
    i=0;
    while(i<m)
    {
        while(-1!=j&&x[i]!=x[j]) j=nexts[j];
        nexts[++i]=++j;
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        scanf("%s",s);
        getnext(s);
        int ans=0;
        for(int i=n;i>=0;i--)
        {
            int k=nexts[i];
            while(k>0)
            {
                ans=(ans+1)%mod;
                k=nexts[k];
            }
        }
        printf("%d\n",(ans+n)%mod);
    }
    return 0;
}

String Problem

解:KMP求最小循环节和字符串的最大(小)表示法。

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
#define mod 10007
char s[1000010];
//char s1[300];
int nexts[1000010];
//char sub[300];
//char res[300];
void getnext(char x[])
{
    int m=strlen(x);
    int i,j;
    j=nexts[0]=-1;
    i=0;
    while(i<m)
    {
        while(-1!=j&&x[i]!=x[j]) j=nexts[j];
        nexts[++i]=++j;
    }
}
int kmp(char x[],char y[])
{
    int n=strlen(y);int m=strlen(x);
    int i,j;
    i=0;j=0;
    while(i<n)
    {
        while(j!=-1&&x[j]!=y[i]) j=nexts[j];
        ++i;++j;
        if(j>=m) return m;
    }
    return 0;
}
int get_index(int flag)
{
    int i,j,k;
    i=k=0;j=1;
    int n;n=strlen(s);
    while(i<n&&j<n&&k<n)
    {

        if(s[(i+k)%n]==s[(j+k)%n]) k++;
        else if(flag==0)
        {
            if(s[(i+k)%n]>s[(j+k)%n]) i=i+k+1;
            else j=j+k+1;
            if(i==j) j++;k=0;
        }
        else if(flag==1)
        {
            if(s[(i+k)%n]>s[(j+k)%n]) j=j+k+1;
            else i=i+k+1;
            if(i==j) j++;k=0;
        }

    }
    return min(i+1,j+1);
}
int main()
{
    while(scanf("%s",s)!=EOF)
    {
        getnext(s);
        int n=strlen(s);
        int small,big,st,bt;
        int len=n-nexts[n];
        small=get_index(0);big=get_index(1);
        st=bt=n/len;
        printf("%d %d %d %d\n",small,st,big,bt);
    }

    return 0;
}

Finding Palindromes

解:容我说一句,这题真**沙比,oj是真的不行。无缘无故报re。写得我最心累的一题。

EXKMP+Trie

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
const int maxn=2e6+7;
char s[maxn];
char t[maxn];
int nexts[maxn];
int extend[maxn];
int pv[2][maxn];
int len[maxn];
void getnext(char x[],int nexts[],int lenx)
{
    nexts[0]=lenx;
    int j=0;
    while(j+1<lenx&&x[j]==x[j+1]) j++;
    nexts[1]=j;
    int k=1;
    for(int i=2;i<lenx;i++)
    {
        int P=nexts[k]+k-1;
        int L=nexts[i-k];
        if(L+i<P+1) nexts[i]=L;
        else
        {
            j=max(0,P-i+1);
            while(i+j<lenx&&x[j]==x[i+j]) j++;
            nexts[i]=j;
            k=i;
        }
    }
}
void exkmp(char x[],int lenx,char y[],int leny,int nexts[],int extend[],int L,int flag)
{
    getnext(x,nexts,lenx);
    int j=0;
    while(j<lenx&&j<leny&&x[j]==y[j]) j++;
    extend[0]=j;
    int k=0;
    for(int i=1;i<leny;i++)
    {
        int P=extend[k]+k-1;
        int Ls=nexts[i-k];
        if(i+Ls<P+1) extend[i]=Ls;
        else
        {
            j=max(0,P-i+1);
            while(i+j<leny&&j<lenx&&y[i+j]==x[j]) j++;
            extend[i]=j;
            k=i;
        }
    }
    for(int i=0;i<leny;i++)
    {
        pv[flag][i+L]=(i+extend[i]==leny);
        //cout<<pv[flag][i+L]<<endl;
    }
}
struct Trie
{
    int ch[maxn][26];
    int ed[maxn];
    int value[maxn];
    int sz;
    void init()
    {
        memset(ch[0],0,sizeof ch[0]);
        ed[0]=0;sz=0;
        value[0]=0;
    }
    void add(char *s,int lens,int L)
    {
        int u=0;
        for(int i=0;i<lens;i++)
        {
            int id=s[i]-'a';
            value[u]+=pv[0][L+i];
            if(!ch[u][id])
            {
                ch[u][id]=++sz;
                memset(ch[sz],0,sizeof ch[sz]);
                value[sz]=0;
                ed[sz]=0;
            }
            u=ch[u][id];
        }
        ed[u]++;
    }
    int finds(char *s,int lens,int L)
    {
        int res=0;
        int u=0;
        for(int i=0;i<lens;i++)
        {
            int id=s[i]-'a';
            u=ch[u][id];
            if(!u) break;
            if((i<lens-1&&pv[1][i+L+1])||i==lens-1)
            {
                res+=ed[u];
            }
        }
        if(u) res+=value[u];
        return res;
    }
}trie;

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int tot=0;
        trie.init();
        memset(pv,0,sizeof pv);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%s",&len[i],s+tot);
            memcpy(t+tot,s+tot,len[i]);
            reverse(t+tot,t+tot+len[i]);
            exkmp(t+tot,len[i],s+tot,len[i],nexts,extend,tot,0);
            exkmp(s+tot,len[i],t+tot,len[i],nexts,extend,tot,1);
            trie.add(s+tot,len[i],tot);
            tot+=len[i];
        }
        ll ans=0;
        tot=0;
        for(int i=1;i<=n;i++)
        {
            ans+=1ll*trie.finds(t+tot,len[i],tot);
            tot+=len[i];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

DNA Sequence

解:这道题有点神了。首次见到fail数组的另类考查吧。AC自动机+矩阵快速幂。先建立关于各个模式串的AC自动机,根据fail数组构建矩阵,因为fail数组保存的就是所有不符合要求的字符串的连接关系,由此建立矩阵。此处再用到一个结论就是节点i到节点j经过K次的方案数 正好就是矩阵的K次幂。

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
const int maxn=1010;
#define LL long long
const int mod=100000;
map<char,int>ma;
struct matrix
{
    LL x[110][110];//使用时按照题目需要改变数组的大小,此为4*4的矩阵
};
matrix mutimatrix(matrix a,matrix b,int t)//该函数用于算两个矩阵相乘
{
    matrix temp;
    memset(temp.x,0,sizeof(temp.x));
    for(int i=0;i<t;i++)
    for(int j=0;j<t;j++)
    for(int k=0;k<t;k++)
    {
        temp.x[i][j]+=a.x[i][k]*b.x[k][j];
        temp.x[i][j]%=mod;
    }
    return temp;
}

matrix k_powmatrix(matrix a,LL n,int t)//矩阵快速幂,和之前的快速幂迭代写法差不多。
{
    matrix temp;
    memset(temp.x,0,sizeof(temp.x));
    for(int i=0;i<t;i++)
    temp.x[i][i]=1;

    while(n)
    {
        if(n&1)
        temp=mutimatrix(temp,a,t);
        a=mutimatrix(a,a,t);
        n>>=1;
    }
    return temp;
}

struct Trie
{
    int nexts[maxn][4];
    int fail[maxn],ed[maxn];
    int root,L;
    int newnode()
    {
        for(int i=0;i<4;i++)
            nexts[L][i]=-1;
        ed[L++]=0;
        return L-1;
    }
    void init()
    {
        ma['A']=0;ma['C']=1;ma['G']=2;ma['T']=3;
        L=0;root=newnode();
    }
    void inserts(char s[])
    {
        int now=root;
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int c=ma[s[i]];
            if(nexts[now][c]==-1)
            {
                nexts[now][c]=newnode();
            }
            now=nexts[now][c];
        }
        ed[now]=1;
    }
    void get_fail()
    {
        queue<int> q;
        fail[root]=root;
        for(int i=0;i<4;i++)
        {
            if(nexts[root][i]==-1)nexts[root][i]=root;
            else
            {
                fail[nexts[root][i]]=root;
                q.push(nexts[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            if(ed[fail[now]]!=0) ed[now]=1;
            for(int i=0;i<4;i++)
            {
                if(nexts[now][i]==-1) nexts[now][i]=nexts[fail[now]][i];
                else
                {
                    fail[nexts[now][i]]=nexts[fail[now]][i];
                    q.push(nexts[now][i]);
                }
            }
        }
    }
    int used[1200];
    void query(char s[])
    {
        memset(used,0,sizeof used);
        int len=strlen(s);
        int now=root;
        for(int i=0;i<len;i++)
        {
            int c=ma[s[i]];
            now=nexts[now][c];
            int tmp=now;
            while(tmp!=root)
            {
                if(ed[tmp]!=0) used[ed[tmp]]++;
                tmp=fail[tmp];
            }
        }
    }
}trie;
char s[200];
int main()
{
    trie.init();
    int m;ll n;
    scanf("%d %lld",&m,&n);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s);
        trie.inserts(s);
    }
    trie.get_fail();
    matrix mt,res;
    memset(mt.x,0,sizeof mt.x);
    for(int i=0;i<trie.L;i++)
    {
        if(trie.ed[i]!=0) continue;
        for(int j=0;j<4;j++)
        {
            if(trie.ed[trie.nexts[i][j]]!=0) continue;
            mt.x[i][trie.nexts[i][j]]++;
        }
    }
    res=k_powmatrix(mt,n,trie.L);
    ll ans=0;
    for(int i=0;i<trie.L;i++)
    {
        ans=(ans+res.x[0][i])%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

考研路茫茫――单词情结

解:这题也很神。和上题类似,ac自动机+矩阵快速幂。确认过眼神,是做不出的题。

本题的一个矩阵要构造成\begin{bmatrix} 26 & 0 \\ 1 & 1 \end{bmatrix}*\begin{bmatrix} f(n-1) &1 \end{bmatrix}  才行。有点儿不理解,换个形式构造就不对了。

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
const int maxn=1010;
#define LL long long
struct Matrix
{
    unsigned long long mat[40][40];
    int n;
    Matrix(){}
    Matrix(int _n)
    {
        n=_n;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                mat[i][j] = 0;
    }
    Matrix operator *(const Matrix &b)const
    {
        Matrix ret = Matrix(n);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                for(int k=0;k<n;k++)
                    ret.mat[i][j]+=mat[i][k]*b.mat[k][j];
        return ret;
    }
};
struct Trie
{
    int nexts[maxn][26];
    int fail[maxn],ed[maxn];
    int root,L;
    int newnode()
    {
        for(int i=0;i<26;i++)
            nexts[L][i]=-1;
        ed[L++]=0;
        return L-1;
    }
    void init()
    {
        L=0;root=newnode();
    }
    void inserts(char s[])
    {
        int now=root;
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int c=s[i]-'a';
            if(nexts[now][c]==-1)
            {
                nexts[now][c]=newnode();
            }
            now=nexts[now][c];
        }
        ed[now]=1;
    }
    void get_fail()
    {
        queue<int> q;
        fail[root]=root;
        for(int i=0;i<26;i++)
        {
            if(nexts[root][i]==-1)nexts[root][i]=root;
            else
            {
                fail[nexts[root][i]]=root;
                q.push(nexts[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            if(ed[fail[now]]!=0) ed[now]=1;
            for(int i=0;i<26;i++)
            {
                if(nexts[now][i]==-1) nexts[now][i]=nexts[fail[now]][i];
                else
                {
                    fail[nexts[now][i]]=nexts[fail[now]][i];
                    q.push(nexts[now][i]);
                }
            }
        }
    }

//    int used[1200];
//    void query(char s[])
//    {
//        memset(used,0,sizeof used);
//        int len=strlen(s);
//        int now=root;
//        for(int i=0;i<len;i++)
//        {
//            int c=s[i]-'a';
//            now=nexts[now][c];
//            int tmp=now;
//            while(tmp!=root)
//            {
//                if(ed[tmp]!=0) used[ed[tmp]]++;
//                tmp=fail[tmp];
//            }
//        }
//    }
}trie;
char s[110];
int main()
{
    int N;ll len;
    while(scanf("%d %lld",&N,&len)!=EOF)
    {
        trie.init();
        for(int i=1;i<=N;i++)
        {
            scanf("%s",s);
            trie.inserts(s);
        }
        trie.get_fail();
        Matrix res1,E;
        res1=Matrix(2);
        res1.mat[0][0]=26;res1.mat[1][1]=res1.mat[1][0]=1;
        E=Matrix(2);
        E.mat[0][0]=E.mat[1][1]=1;
        ll tmp=len;
        while(tmp)
        {
            if(tmp&1) E=E*res1;
            res1=res1*res1;
            tmp>>=1;
        }
        unsigned long long ans1=E.mat[0][0]+E.mat[1][0];
        ans1--;
        res1=Matrix(trie.L+1);
        for(int i=0;i<trie.L;i++)
        {
            if(trie.ed[i]==1) continue;
            for(int j=0;j<26;j++)
            {
                if(trie.ed[trie.nexts[i][j]]==1) continue;
                res1.mat[i][trie.nexts[i][j]]++;
            }
        }
        E=Matrix(trie.L+1);
        for(int i=0;i<=trie.L;i++)
        {
            E.mat[i][i]=1;
            res1.mat[i][trie.L]=1;
        }
        tmp=len;
        while(tmp)
        {
            if(tmp&1) E=E*res1;
            res1=res1*res1;
            tmp>>=1;
        }
        unsigned long long ans2=0;
        for(int i=0;i<E.n;i++) ans2+=E.mat[0][i];
        ans2--;
        unsigned long long ans=ans1-ans2;
        printf("%llu\n",ans);
    }
    return 0;
}

Wireless Password

解:再次见识AC自动机的各种考法。AC自动机+DP。建立关于给定的模式串的AC自动机后,根据tried图,可以定义dp[i][j][k]为当长度为i,走到节点j时,单词情况为k的方案数。

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
const int maxn=110;
#define LL long long
#define mod 20090717

struct Trie
{
    int nexts[maxn][26];
    int fail[maxn],ed[maxn];
    int root,L;
    ll dp[30][110][1030];
    int newnode()
    {
        for(int i=0;i<26;i++)
            nexts[L][i]=-1;
        ed[L++]=0;
        return L-1;
    }
    void init()
    {
        L=0;root=newnode();
    }
    void inserts(char s[],int id)
    {
        int now=root;
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int c=s[i]-'a';
            if(nexts[now][c]==-1)
            {
                nexts[now][c]=newnode();
            }
            now=nexts[now][c];
        }
        ed[now]|=(1<<id);
    }
    void get_fail()
    {
        queue<int> q;
        fail[root]=root;
        for(int i=0;i<26;i++)
        {
            if(nexts[root][i]==-1)nexts[root][i]=root;
            else
            {
                fail[nexts[root][i]]=root;
                q.push(nexts[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            if(ed[fail[now]]!=0) ed[now]|=ed[fail[now]];
            for(int i=0;i<26;i++)
            {
                if(nexts[now][i]==-1) nexts[now][i]=nexts[fail[now]][i];
                else
                {
                    fail[nexts[now][i]]=nexts[fail[now]][i];
                    q.push(nexts[now][i]);
                }
            }
        }
    }
    ll solve(int n,int m,int K)
    {
        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]>0)
                    for(int ch=0;ch<26;ch++)
                    {
                        int ni=i+1;int nj=nexts[j][ch];int nk=k|ed[nexts[j][ch]];
                        dp[ni][nj][nk]+=dp[i][j][k];
                        dp[ni][nj][nk]%=mod;
                    }
                }
            }
        }
        ll ans=0;
        for(int i=0;i<(1<<m);i++)
        {
            if(__builtin_popcount(i)<K) continue;
            for(int j=0;j<L;j++)
            {
                ans+=dp[n][j][i];
                ans%=mod;
            }
        }
        return ans;
    }

}trie;
char s[110];
int main()
{
    int N,M,K;
    while(scanf("%d %d %d",&N,&M,&K)!=EOF)
    {
        if(N==0&&M==0&&K==0) break;
        trie.init();
        for(int i=0;i<M;i++)
        {
            scanf("%s",s);
            trie.inserts(s,i);
        }
        trie.get_fail();
        ll ans=trie.solve(N,M,K);
        printf("%lld\n",ans);
    }
    return 0;
}

Ring

解:AC自动机+dp。dp[i][j]:长度为i走到节点j的最大价值。dp[i+1][j]=max(dp[i][j],dp[i][fa(j)]+ed[fa(j)])

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
const int maxn=2100;
#define LL long long
#define mod 20090717
//struct Matrix
//{
//    long long mat[110][110];
//    int n;
//    Matrix(){}
//    Matrix(int _n)
//    {
//        n=_n;
//        for(int i=0;i<n;i++)
//            for(int j=0;j<n;j++)
//                mat[i][j] = 0;
//    }
//    Matrix operator *(const Matrix &b)const
//    {
//        Matrix ret = Matrix(n);
//        for(int i=0;i<n;i++)
//            for(int j=0;j<n;j++)
//                for(int k=0;k<n;k++)
//                    ret.mat[i][j]+=mat[i][k]*b.mat[k][j];
//        return ret;
//    }
//};
int cost[110];
struct Trie
{
    int nexts[maxn][26];
    int fail[maxn],ed[maxn];
    int root,L;
    int dp[110][2100];
    string path[110][2100];
    int newnode()
    {
        for(int i=0;i<26;i++)
            nexts[L][i]=-1;
        ed[L++]=0;
        return L-1;
    }
    void init()
    {
        L=0;root=newnode();
    }
    bool cmp(string s,string t)
    {
        if(t=="")return false;
        if(s.size()>t.size()) return true;
        if(s.size()<t.size()) return false;
        return s>t;
    }
    void inserts(char s[],int id)
    {
        int now=root;
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int c=s[i]-'a';
            if(nexts[now][c]==-1)
            {
                nexts[now][c]=newnode();
            }
            now=nexts[now][c];
        }
        ed[now]=cost[id];
    }
    void get_fail()
    {
        queue<int> q;
        fail[root]=root;
        for(int i=0;i<26;i++)
        {
            if(nexts[root][i]==-1)nexts[root][i]=root;
            else
            {
                fail[nexts[root][i]]=root;
                q.push(nexts[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            //if(ed[fail[now]]!=0) ed[now]|=ed[fail[now]];
            for(int i=0;i<26;i++)
            {
                if(nexts[now][i]==-1) nexts[now][i]=nexts[fail[now]][i];
                else
                {
                    fail[nexts[now][i]]=nexts[fail[now]][i];
                    q.push(nexts[now][i]);
                }
            }
        }
    }
    string solve(int n)
    {
        memset(dp,-1,sizeof dp);
        dp[0][0]=0;int res=0;
        for(int i=0;i<=n;i++)
            for(int j=0;j<=L;j++)
                path[i][j]="";
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<L;j++)
            {
                if(dp[i][j]==-1) continue;
                for(int k=0;k<26;k++)
                {
                    int ni=i+1;int nj=nexts[j][k];
                    if(dp[ni][nj]<dp[i][j]+ed[nexts[j][k]])
                    {
                        dp[ni][nj]=dp[i][j]+ed[nexts[j][k]];
                        path[ni][nj]=path[i][j]+(char)(k+'a');
                    }
                    else if(dp[ni][nj]==dp[i][j]+ed[nexts[j][k]])
                    {
                        if(cmp(path[ni][nj],path[i][j]+(char)(k+'a')))
                        {
                            path[ni][nj]=path[i][j]+(char)(k+'a');
                        }
                    }
                }
                res=max(res,dp[i][j]);
                //cout<<dp[i][j]<<" ";
            }
            //cout<<endl;
        }
        string ans="";
        if(res==0) return ans;
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<L;j++)
            {
                if(dp[i][j]==res)
                {
                    if(ans=="") ans=path[i][j];
                    else if(cmp(ans,path[i][j])) ans=path[i][j];
                }
            }
        }
        return ans;
    }

}trie;
char s[110][50];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int N,M;
        scanf("%d %d",&N,&M);
        trie.init();
        for(int i=0;i<M;i++)
        {
            scanf("%s",s[i]);
            //trie.inserts(s,i);
        }
        for(int i=0;i<M;i++) scanf("%d",&cost[i]);
        for(int i=0;i<M;i++) trie.inserts(s[i],i);
        trie.get_fail();
        string ans=trie.solve(N);
        cout<<ans<<'\n';
    }
    return 0;
}

DNA repair

解:说实话我还是不能独立的做出这种题。通过trie图,构造一个字符串,这个字符串不会包含任何模式串。dp[i][j]:表示当字符长度为i,走到节点j的最小修改数。dp[i][next[j][k]]=min(dp[i][j],dp[i-1][j]+(s[i]!=k);

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
const int maxn=2100;
#define LL long long
#define mod 20090717
#define inf 0x3f3f3f3f

char s[1100];
struct Trie
{
    int nexts[maxn][26];
    int fail[maxn],ed[maxn];
    int root,L;
    int dp[1010][1010];
    map<char,int>ma;
    char num[4];
    int newnode()
    {
        for(int i=0;i<4;i++)
            nexts[L][i]=-1;
        ed[L++]=0;
        return L-1;
    }
    void init()
    {
        L=0;root=newnode();
        ma['A']=0;ma['C']=1;ma['G']=2;ma['T']=3;
        num[0]='A';num[1]='C';num[2]='G';num[3]='T';
    }
    bool cmp(string s,string t)
    {
        if(t=="")return false;
        if(s.size()>t.size()) return true;
        if(s.size()<t.size()) return false;
        return s>t;
    }
    void inserts(char s[])
    {
        int now=root;
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int c=ma[s[i]];
            if(nexts[now][c]==-1)
            {
                nexts[now][c]=newnode();
            }
            now=nexts[now][c];
        }
        ed[now]=1;
    }
    void get_fail()
    {
        queue<int> q;
        fail[root]=root;
        for(int i=0;i<4;i++)
        {
            if(nexts[root][i]==-1)nexts[root][i]=root;
            else
            {
                fail[nexts[root][i]]=root;
                q.push(nexts[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            if(ed[fail[now]]!=0) ed[now]=1;
            for(int i=0;i<4;i++)
            {
                if(nexts[now][i]==-1) nexts[now][i]=nexts[fail[now]][i];
                else
                {
                    fail[nexts[now][i]]=nexts[fail[now]][i];
                    q.push(nexts[now][i]);
                }
            }
        }
    }
    int solve(int n)
    {
        for(int i=0;i<=n;i++)
            for(int j=0;j<=L;j++)
                dp[i][j]=inf;
        dp[0][0]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<L;j++)
            {
                if(dp[i-1][j]==inf) continue;
                for(int k=0;k<4;k++)
                {
                    if(ed[nexts[j][k]]==1) continue;
                    int ni=i;int nj=nexts[j][k];
                    dp[ni][nj]=min(dp[ni][nj],dp[i-1][j]+(s[i]!=num[k]));//当构造的串的当前位与给定的串的对应位不一样时就要加1表示改变。
                }
            }
        }
        int ans=inf;
        for(int i=0;i<L;i++) ans=min(ans,dp[n][i]);
        if(ans==inf) return -1;
        return ans;
    }
}trie;

int main()
{
    int N;
    int kcase=0;
    while(scanf("%d",&N)!=EOF)
    {
        if(N==0) break;
        trie.init();
        for(int i=0;i<N;i++)
        {
            scanf("%s",s);
            trie.inserts(s);
        }
        trie.get_fail();
        scanf("%s",s+1);
        int len=strlen(s+1);
        int ans=trie.solve(len);
        printf("Case %d: %d\n",++kcase,ans);
    }
    return 0;
}

Lost's revenge

解:还是AC自动机+DP。dp[i][j]:走到节点i,四个碱基的使用情况为j的最大数量。j表示各个碱基数量。可以使用哈希的形式表示这所有的情况。dp[nexts[i][ch]][S1]=max(dp[i][S2]+ed[nexts[i][ch]],dp[nexts[i][ch]][S1])

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
const int maxn=2100;
#define LL long long
#define mod 20090717
#define inf 0x3f3f3f3f
char s[1100];
struct Trie
{
    int nexts[maxn][26];
    int fail[maxn],ed[maxn];
    int root,L;
    int dp[600][15000];
    int hashs[45][45][45][45];
    int cnt;
    //map<char,int>ma;
    int num[4];
    int newnode()
    {
        for(int i=0;i<4;i++)
            nexts[L][i]=-1;
        ed[L++]=0;
        return L-1;
    }
    void init()
    {
        L=0;root=newnode();
        //ma['A']=0;ma['C']=1;ma['G']=2;ma['T']=3;
        //num[0]='A';num[1]='C';num[2]='G';num[3]='T';
        num[0]=num[1]=num[2]=num[3]=0;
        cnt=0;
    }
    int ma(char x)
    {
        if(x=='A') return 0;
        if(x=='C') return 1;
        if(x=='G') return 2;
        if(x=='T') return 3;
    }
    bool cmp(string s,string t)
    {
        if(t=="")return false;
        if(s.size()>t.size()) return true;
        if(s.size()<t.size()) return false;
        return s>t;
    }
    void inserts(char s[])
    {
        int now=root;
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int c=ma(s[i]);
            if(nexts[now][c]==-1)
            {
                nexts[now][c]=newnode();
            }
            now=nexts[now][c];
        }
        ed[now]++;
    }
    void get_fail()
    {
        queue<int> q;
        fail[root]=root;
        for(int i=0;i<4;i++)
        {
            if(nexts[root][i]==-1)nexts[root][i]=root;
            else
            {
                fail[nexts[root][i]]=root;
                q.push(nexts[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            if(ed[fail[now]]!=0) ed[now]+=ed[fail[now]];
            for(int i=0;i<4;i++)
            {
                if(nexts[now][i]==-1) nexts[now][i]=nexts[fail[now]][i];
                else
                {
                    fail[nexts[now][i]]=nexts[fail[now]][i];
                    q.push(nexts[now][i]);
                }
            }
        }
    }
    int solve(int n)
    {
        memset(dp,-1,sizeof dp);
        for(int i=1;i<=n;i++)
            num[ma(s[i])]++;

        for(int i=0;i<=num[0];i++)
            for(int j=0;j<=num[1];j++)
                for(int k=0;k<=num[2];k++)
                    for(int h=0;h<=num[3];h++)
                        hashs[i][j][k][h]=cnt++;

        dp[0][0]=0;
        for(int i=0;i<=num[0];i++)
        {
            for(int j=0;j<=num[1];j++)
            {
                for(int k=0;k<=num[2];k++)
                {
                    for(int h=0;h<=num[3];h++)
                    {
                        if(i+j+k+h==0) continue;
                        for(int ti=0;ti<L;ti++)
                        {
                                for(int ch=0;ch<4;ch++)
                                {
                                    int tmp1=hashs[i][j][k][h];
                                    int tmp2;
                                    if(ch==0){
                                        if(i==0) continue;
                                        else tmp2=hashs[i-1][j][k][h];
                                    }
                                    else if(ch==1)
                                    {
                                        if(j==0) continue;
                                        else tmp2=hashs[i][j-1][k][h];
                                    }
                                    else if(ch==2)
                                    {
                                        if(k==0) continue;
                                        else tmp2=hashs[i][j][k-1][h];
                                    }
                                    else
                                    {
                                        if(h==0) continue;
                                        else tmp2=hashs[i][j][k][h-1];
                                    }
                                    if(dp[ti][tmp2]==-1) continue;
                                    dp[nexts[ti][ch]][tmp1]=max(dp[ti][tmp2]+ed[nexts[ti][ch]],dp[nexts[ti][ch]][tmp1]);
                                }
                        }
                    }
                }
            }
        }
        int res=0;int st=hashs[num[0]][num[1]][num[2]][num[3]];
        for(int i=0;i<L;i++) res=max(res,dp[i][st]);
        return res;
    }
}trie;

int main()
{
    int N;
    int kcase=0;
    while(scanf("%d",&N)!=EOF)
    {
        if(N==0) break;
        trie.init();
        for(int i=0;i<N;i++)
        {
            scanf("%s",s);
            trie.inserts(s);
        }
        trie.get_fail();
        scanf("%s",s+1);
        int len=strlen(s+1);
        int ans=trie.solve(len);
        printf("Case %d: %d\n",++kcase,ans);
    }
    return 0;
}

Resource Archiver

解:这题就比之前的更牛逼一点了。AC自动机+DP+BFS。确实是想不出,到了比赛这种题也是没时间去做的。把给出来的好串和坏串都加入到AC自动机的字典数里。由于内存不允许,不能直接定义dp[i][j]表示走到节点i时构造的字符串的包含好串的情况为j。但可以发现,n远小于m,即好的点远小于总数。将所有好串的尾节点重新编号,并且求出两两之间的最短距离。则dp[i][j]即表示走到好的节点i时的情况为j。dp[i][ j | ma[j] ]=min(dp[i][ j | ma[j] ] , dp[k][j]+cost[k][i]);

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
const int maxn=60000;
#define LL long long
#define mod 20090717
#define inf 0x3f3f3f3f

char s[1100];
struct Trie
{
    int nexts[maxn][2];
    int fail[maxn],ed[maxn],vir[maxn];
    int root,L;
    int dp[600][15000];
    int dist[maxn];bool vis[maxn];
    int tot;int ma[500];
    int cost[550][550];
    int newnode()
    {
        for(int i=0;i<2;i++)
            nexts[L][i]=-1;
        vir[L]=0;ed[L++]=0;
        return L-1;
    }
    void init()
    {
        tot=0;
        L=0;root=newnode();
    }
    void inserts(char s[],int id,int flag)
    {
        int now=root;
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int c=s[i]-'0';
            if(nexts[now][c]==-1)
            {
                nexts[now][c]=newnode();
            }
            now=nexts[now][c];
        }
        if(flag) vir[now]=1;
        else ed[now]|=(1<<id);
    }
    void get_fail()
    {
        queue<int> q;
        fail[root]=root;
        for(int i=0;i<2;i++)
        {
            if(nexts[root][i]==-1)nexts[root][i]=root;
            else
            {
                fail[nexts[root][i]]=root;
                q.push(nexts[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            ed[now]|=ed[fail[now]];
            vir[now]+=vir[fail[now]];
            for(int i=0;i<2;i++)
            {
                if(nexts[now][i]==-1) nexts[now][i]=nexts[fail[now]][i];
                else
                {
                    fail[nexts[now][i]]=nexts[fail[now]][i];
                    q.push(nexts[now][i]);
                }
            }
        }
    }
    void bfs(int st)
    {
        queue<int> q;
        while(!q.empty()) q.pop();
        memset(vis,0,sizeof vis);
        for(int i=0;i<L;i++) dist[i]=inf;
        q.push(ma[st]);
        dist[ma[st]]=0;vis[ma[st]]=1;
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            for(int i=0;i<2;i++)
            {
                int nex=nexts[now][i];
                if(vis[nex])continue;
                if(vir[nex]) continue;
                dist[nex]=dist[now]+1;
                vis[nex]=1;
                q.push(nex);
            }
        }
        for(int i=0;i<tot;i++)
        {
            cost[st][i]=dist[ma[i]];
        }
    }
    void get_new_node()
    {
        ma[tot++]=0;
        for(int i=0;i<L;i++)
        {
            if(ed[i]!=0) ma[tot++]=i;
        }
        for(int i=0;i<tot;i++)
        {
            bfs(i);
        }
    }
    int solve(int n)
    {
        get_new_node();
        for(int i=0;i<tot;i++)
            for(int j=0;j<(1<<n);j++)
                dp[i][j]=inf;
        dp[0][0]=0;
        for(int j=0;j<(1<<n);j++)
        {
            for(int i=0;i<tot;i++)
            {
                if(dp[i][j]>=inf) continue;
                for(int k=0;k<tot;k++)
                {
                    if(i==k) continue;
                    dp[k][j|ed[ma[k]]]=min(dp[k][j|ed[ma[k]]],dp[i][j]+cost[i][k]);
                }
            }
        }
        int res=inf;
        for(int i=0;i<tot;i++) res=min(res,dp[i][(1<<n)-1]);
        return res;
    }
}trie;

int main()
{
    int n,m;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        if(n==0&&m==0) break;
        trie.init();
        for(int i=0;i<n;i++)
        {
            scanf("%s",s);
            trie.inserts(s,i,0);
        }
        for(int i=0;i<m;i++)
        {
            scanf("%s",s);
            trie.inserts(s,i,1);
        }
        trie.get_fail();
        int ans=trie.solve(n);
        printf("%d\n",ans);
    }
    return 0;
}

之前也没有怎么学过字符串的算法,终于在前几天学了AC自动机。在懂得差不多的原理之后(其实有些部分还是模糊的)。正所谓纸上得来终觉浅,还是要通过做题去发现自己还有哪些不懂以及这个算法它能怎么考。通过这几天的bin巨的专题抗压训练后可以发现,基本不会在比赛中会直接单独考查AC自动机的多模匹配的作用,更多的是在它通过fail数组构造得出的一个trie图上做文章。遇到的类型有:①给定一些模式串问能有多少种构造方法得到不包含给定串的字符串,这种题通常就是通过trie图,判断不可以走到模式串的结尾来构造一个矩阵,通过矩阵快速幂得到方案数。②其他的就是通过各种DP乱搞的方式,但有个规律是dp的其中一维是和trie图上的节点有关(在我做过的题看来是这样的)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值