bzoj2085: [Poi2010]Hamsters(hash+矩阵快速幂)

10 篇文章 0 订阅
8 篇文章 0 订阅

传送门
题意:给定n个长度总和不超过1e5的字符串,求一个最短的母串,使所有字符串的出现次数之和=m 这n个字符串保证不互相包含,n≤200
思路:
由于保证字符串两两不相互包含。
因此我们可以考虑 d p dp dp
f i , j f_{i,j} fi,j表示所有字符串出现次数之和为 i i i,结尾的字符串为第 j j j个整个字符串的最短长度。
然后可以枚举字符串转移。
发现可以写成矩阵转移的形式然后用矩阵快速幂优化即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
    static char buf[rlen],*ib,*ob;
    (ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
    return ib==ob?-1:*ib++;
}
inline int read(){
    int ans=0;
    char ch=gc();
    while(!isdigit(ch))ch=gc();
    while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
    return ans;
}
inline int Read(char*s){
    int top=0;
    char ch=gc();
    while(!isalpha(ch))ch=gc();
    while(isalpha(ch))s[++top]=ch,ch=gc();
    return top;
}
inline char get_a_char(){
    char ch=gc();
    while(!isalpha(ch))ch=gc();
    return ch;
}
const int N=205;
int n,m;
const int mod1=1e9+9;
typedef long long ll;
typedef unsigned long long Ull;
const ll inf=1e18;
const Ull bas=311;
inline int add1(const int&a,const int&b){return a+b>=mod1?a+b-mod1:a+b;}
inline int dec1(const int&a,const int&b){return a>=b?a-b:a-b+mod1;}
inline int mul1(const int&a,const int&b){return (ll)a*b%mod1;}
struct Hash_string{
    vector<Ull>pw1,s1;
    vector<int>pw2,s2;
    int len;
    inline void init(char*s,int n){
        len=n;
        pw1.resize(n+1),pw2.resize(n+1),s1.resize(n+1),s2.resize(n+1);
        pw1[0]=pw2[0]=1;
        for(ri i=1;i<=n;++i){
            pw1[i]=pw1[i-1]*bas;
            s1[i]=s1[i-1]*bas+(Ull)s[i];
            pw2[i]=mul1(pw2[i-1],bas);
            s2[i]=add1(mul1(s2[i-1],bas),(int)s[i]);
        }
    }
    inline Ull get1(int l,int r){return s1[r]-s1[l-1]*pw1[r-l+1];}
    inline Ull get2(int l,int r){return dec1(s2[r],mul1(s2[l-1],pw2[r-l+1]));}
}S[N];
char s[100005];
typedef long long ll;
struct Mat{
    ll a[N][N];
    Mat(){for(ri i=1;i<=n;++i)for(ri j=1;j<=n;++j)a[i][j]=inf;}
    friend inline Mat operator*(const Mat&a,const Mat&b){
        Mat ret;
        for(ri i=1;i<=n;++i)for(ri k=1;k<=n;++k)for(ri j=1;j<=n;++j)ret.a[i][j]=min(ret.a[i][j],a.a[i][k]+b.a[k][j]);
        return ret;
    }
    friend inline Mat operator^(Mat a,int p){
        Mat ret=a;
        for(--p;p;p>>=1,a=a*a)if(p&1)ret=ret*a;
        return ret;
    }
}trans;
inline int init(int a,int b){
    int n=S[a].len,m=S[b].len;
    int up=min(n,m),ret=0;
    if(a==b)--up;
    for(ri i=1;i<=up;++i)if(S[a].get1(n-i+1,n)==S[b].get1(1,i)&&S[a].get2(n-i+1,n)==S[b].get2(1,i))ret=i;
    return m-ret;
}
int main(){
    n=read(),m=read();
    ll mn=inf;
    for(ri len,i=1;i<=n;++i){
        len=Read(s);
        mn=min(mn,(ll)len);
        S[i].init(s,len);
    }
    if(m==1)return cout<<mn,0;
    for(ri i=1;i<=n;++i)for(ri j=1;j<=n;++j)trans.a[i][j]=init(i,j);
    trans=trans^(m-1);
    ll ans=inf;
    for(ri i=1;i<=n;++i)for(ri j=1;j<=n;++j)ans=min(ans,S[i].len+trans.a[i][j]);
    cout<<ans;
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值