BZOJ2085 [Poi2010]Hamsters

好题!

又学习了一下kmp的用途qwq

我们观察到n非常小

所以肯定可以通过一些东西来求第i个字符串后面接第j个字符串最少需要接几个字符

那就是最长的i的后缀和j的前缀相同的长度

那么我们可以用f[i][j]表示len[j]-x [x表示上面的那个长度]

然后我们现在就是要求走k步的最小长度和

有个特殊的算法就是解决这个的就是倍增floyd

具体倍增floyd的实现 类似于矩阵乘法

理解一下就是说dis每一次都是扩大一倍【走的路的条数

然后方便一点的实现就是建一个虚点0 然后向其他所有点连接长度为len[x]的一条边 这样就不需要额外处理了

//Love and Freedom.
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
#define inf 20021225
#define N 201
#define LEN 100010 
#define LG 30
using namespace std;

char ch[N][LEN]; int nxt[LEN],len[N]; int n; ll m;
struct floyd
{
    ll dis[N][N];
    floyd(){memset(dis,48,sizeof(dis));}
    inline ll* operator[](int i){return dis[i];}
    friend floyd operator*(floyd &x,floyd &y)
    {
        floyd z;
        for(int i=0;i<=n;i++)    for(int j=0;j<=n;j++)
            for(int k=0;k<=n;k++)
                z[i][j]=min(z[i][j],x[i][k]+y[k][j]);
        return z;
    }
}f,d,ans;
int calc(int x,int y)
{
    int lx=len[x],ly=len[y];
    for(int i=1;i<=lx;i++)    nxt[i]=0;
    for(int i=2;i<=lx;i++)
    {
        nxt[i]=nxt[i-1];
        while(nxt[i] && ch[x][i]!=ch[x][nxt[i]+1])
            nxt[i]=nxt[nxt[i]];
        if(ch[x][i]==ch[x][nxt[i]+1])    nxt[i]++;
    }
    if(x==y)    return lx-nxt[lx];
    int j=0;
    for(int i=1;i<=ly;i++)
    {
        while(j && ch[x][j+1]!=ch[y][i])
            j=nxt[j];
        if(j<lx && ch[x][j+1]==ch[y][i])    j++;
    }
    return lx-j;
}
floyd ksm(floyd bs,ll mi)
{
    floyd ans; for(int i=0;i<=n;i++)    ans[i][i]=0;
    while(mi)
    {
        if(mi&1)    ans=ans*bs;
        bs=bs*bs; mi>>=1;
    }
    return ans;
}
int main()
{
    scanf("%d%lld",&n,&m); f=floyd();
    for(int i=1;i<=n;i++)
        scanf("%s",ch[i]+1),d[0][i]=len[i]=strlen(ch[i]+1);
    for(int i=1;i<=n;i++)    for(int j=1;j<=n;j++)
        d[j][i]=calc(i,j);
    ans=ksm(d,m); ll aans=1ll*inf*inf;
    for(int i=1;i<=n;i++)    aans=min(aans,ans[0][i]);
    printf("%lld\n",aans);
    return 0;
}
View Code

 

posted @ 2019-06-27 08:22  寒雨微凝  阅读(193)  评论(0编辑  收藏  举报