HDU2243

HDU2243

题意

求长度为m且包含1-n个子串的种类数.

思路

首先计算 26 + 2 6 2 + . . . . + 2 6 m 26+26^2+....+26^m 26+262+....+26m,构造矩阵为
[ s u m ( m + 1 ) 1 ] = [ s u m ( m ) ( 初 始 s u m ( 1 ) = 26 ) 1 ] ∗ [ 26 0 26 1 ] \left[ \begin{matrix} sum(m+1) & 1 \end{matrix} \right]= \left[ \begin{matrix} sum(m)(初始sum(1)=26) & 1 \end{matrix} \right] * \left[ \begin{matrix} 26 & 0 \\ 26 & 1 \end{matrix} \right] [sum(m+1)1]=[sum(m)(sum(1)=26)1][262601]
最后实求矩阵的m次方之和,就是在矩阵多开一列,最后一列全为0,如下就可求得矩阵第一列之和.(貌似是这样,欢迎指错).还有就是l< 2 31 2^{31} 231,所以不能算到l+1次幂,算到l次幂就行了.
[ A m + 1 s u m ( A m ) + 1 0 1 ] = [ A 1 0 1 ] m + 1 \left[ \begin{matrix} A^{m+1} & sum(A^{m})+1 \\ 0 &1 \end{matrix} \right]= \left[ \begin{matrix} A & 1 \\ 0 & 1 \end{matrix} \right]^{m+1} [Am+10sum(Am)+11]=[A011]m+1
最后把第一行全加起来减去1就是了不包含任何一个给定串的答案,再用总的结果减去不包含的就是包含的了.

代码

#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
#include<iostream>
using namespace std;
typedef unsigned long long ll;
int tot;
const int mod=100000;
int _size;

struct Matix{
    ll m[110][110];
    Matix(){
        memset(m,0,sizeof(m));
    }
    Matix operator*(const Matix &m2){
        Matix t;
        for(int i=0;i<_size;++i) 
            for(int j=0;j<_size;++j)
                for(int k=0;k<_size;++k)
                    t.m[i][j]=(t.m[i][j]+m[i][k]*m2.m[k][j]);
        return t;
    };
    Matix operator^(ll k) {
        Matix x=*this,ans;
        ans.m[0][0]=ans.m[1][1]=1;
        while(k) {
            if(k&1)
                ans=ans*x;
            x=x*x;
            k>>=1;
        }
        return ans;
    }
}g,res;
struct Trie{
    int next[105010][30],fail[105010];
    bool end[105010];
    int root,L;
    int idx(char s){
        return s-'a';
    }
    int newnode(){
        for(int i=0;i<26;i++) next[L][i]=-1;
        end[L++]=0;
        return L-1;
    }
    void init(){
        L=0;
        root=newnode();
    }
    void insert(char buf[]){
        int len=strlen(buf);
        int now=root;
        for(int i=0;i<len;i++){
            int x=idx(buf[i]);
            if(next[now][x]==-1)
                next[now][x]=newnode();
            now=next[now][x];
        }
        end[now]=1;
    }
    void build(){
        queue<int> Q;
        fail[root]=root;
        for(int i=0;i<26;i++){
            if(next[root][i]==-1)
                next[root][i]=root;
            else {
                fail[next[root][i]]=root;
                Q.push(next[root][i]);
            }
        }
        while(!Q.empty()){
            int now=Q.front();
            if(end[fail[now]]) end[now]=1;
            Q.pop();
            for(int i=0;i<26;i++){
                if(next[now][i]==-1)
                    next[now][i]=next[fail[now]][i];
                else {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
            }
        }
        tot=L;
        for(int i=0;i<L;i++){
            if(end[i]) continue;
            for(int j=0;j<26;j++){
                if(end[next[i][j]]) continue;
                else g.m[i][next[i][j]]++;
            }
        }
        
    }
    
} ac;

char buf[1000010];
int main(){
    // printf("%lld\n",0ll+26+26*26+26*26*26);
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        int _m=m-1;
        _size=2;
        res.m[0][0]=26;res.m[0][1]=1;
        g.m[0][0]=26,g.m[0][1]=0,g.m[1][0]=26,g.m[1][1]=1;
        // printf("----------------------\n");
        // for(int i=0;i<_size;i++){
        //     for(int j=0;j<_size;j++)
        //         printf("%d ",res.m[i][j]);
        //     puts("");
        // }
        // printf("----------------------\n");
        // for(int i=0;i<_size;i++){
        //     for(int j=0;j<_size;j++)
        //         printf("%d ",g.m[i][j]);
        //     puts("");
        // }
        // printf("----------------------\n");
        while(_m){
            if(_m&1) res=res*g;
            g=g*g;
            _m>>=1;
        }
        ll ans1=res.m[0][0];
        for(int i=0;i<_size;i++)
            for(int j=0;j<_size;j++)
                res.m[i][j]=0,g.m[i][j]=0;
        // cout<<"ans1="<<ans1<<endl;
        ac.init();
        for(int i=1;i<=n;i++){
            scanf("%s",buf);
            ac.insert(buf);
        }
        ac.build();
        _size=tot+1;
        for(int i=0;i<=tot;i++)
            res.m[i][i]=1,g.m[i][tot]=1;
        // printf("----------------------\n");
        // for(int i=0;i<_size;i++){
        //     for(int j=0;j<_size;j++)
        //         printf("%d ",res.m[i][j]);
        //     puts("");
        // }
        // printf("----------------------\n");
        // for(int i=0;i<_size;i++){
        //     for(int j=0;j<_size;j++)
        //         printf("%d ",g.m[i][j]);
        //     puts("");
        // }
        // printf("----------------------\n");
        while(m){
            if(m&1) res=res*g;
            g=g*g;
            m>>=1;
        }
        ll ans2=-1;
        for(int i=0;i<=_size;i++){
            ans2+=res.m[0][i];
        }
        printf("%llu\n",ans1-ans2);
    }
    
    // system("pause");
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值