HDU 2243 考研路茫茫――单词情结(AC自动机+矩阵快速幂)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2243

 

题目大意:

  给m个字符串,求长度不超过n且包括这m个字符串的字符串个数。

 

题目思路:

  推荐博客:传送门
  跟POJ 2778非常像,之前那个题是求不包括的,这里是求包括的,那非常自然就想到可以用所有的情况数减去不包括的情况数。所有的情况数就是长度为1的所有情况+长度为2的…+长度为n的,也就是 26 + 2 6 2 + . . . 2 6 n 26+26^2+...26^n 26+262+...26n,对于矩阵也同样如此,矩阵的获得方法可以看我前面POJ2778的题解:传送门,接下来就是说明如何求得这个次数变大的和
  这其实是一个矩阵快速幂的经典问题,对于矩阵
∣ A 1 0 1 ∣ ∗ ∣ A 1 0 1 ∣ = ∣ A 2 A + 1 0 1 ∣ \left\vert\begin{matrix} A & 1\\ 0 & 1 \end{matrix} \right\vert* \left\vert\begin{matrix} A & 1\\ 0 & 1 \end{matrix} \right\vert= \left\vert\begin{matrix} A^2 & A+1\\ 0 & 1 \end{matrix} \right\vert A011A011=A20A+11
  如果这还不够明显,就再来一次!
∣ A 2 A + 1 0 1 ∣ ∗ ∣ A 1 0 1 ∣ = ∣ A 3 A 2 + A + 1 0 1 ∣ \left\vert\begin{matrix} A^2 &A+1\\ 0 & 1 \end{matrix} \right\vert* \left\vert\begin{matrix} A & 1\\ 0 & 1 \end{matrix} \right\vert= \left\vert\begin{matrix} A^3 & A^2+A+1\\ 0 & 1 \end{matrix} \right\vert A20A+11A011=A30A2+A+11
  就是这么神奇!右上角就是我们需要的东西!矩阵要用的话,将1换成单位矩阵即可。

 

以下是代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>

using namespace std;
#define ll unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
const int MAXN = 55;
const int MOD = 100000;
char s[MAXN];
int trie[MAXN][26],tot,pos[MAXN],fail[MAXN],num[MAXN];
ll a[MAXN][MAXN],f[MAXN][MAXN];
void mul(ll f[MAXN][MAXN],ll a[MAXN][MAXN]){
    ll c[MAXN][MAXN];
    memset(c,0,sizeof(c));
    rep(i,1,tot){
        rep(j,1,tot){
            rep(k,1,tot){
                c[i][j]=c[i][j]+f[i][k]*a[k][j];
            }
        }
    }
    memcpy(f,c,sizeof(c));
}
void mulself(ll a[MAXN][MAXN]){
    ll c[MAXN][MAXN];
    memset(c,0,sizeof(c));
    rep(i,1,tot){
        rep(j,1,tot){
            rep(k,1,tot){
                c[i][j]=c[i][j]+(ll)a[i][k]*a[k][j];
            }
        }
    }
    memcpy(a,c,sizeof(c));
}
void Insert(int x){
    int len=strlen(s),p=1;
    rep(i,0,len-1){
        int ch=s[i]-'a';
        if(!trie[p][ch])trie[p][ch]=++tot;
        p=trie[p][ch];
    }
    num[p]++;
}
queue<int>q;
vector<int>v[MAXN];
void dfs(int u,int x){
    int len=v[u].size();
    if(x==1)num[u]=1;
    rep(i,0,len-1){
        int y=v[u][i];
        if(num[u])dfs(y,1);
        else dfs(y,x);
    }
}
int main()
{
    int m,n;
    while(cin>>m>>n){
        tot=1;
        memset(trie,0,sizeof(trie));
        memset(num,0,sizeof(num));
        rep(i,1,m){
            scanf("%s",s);
            Insert(i);
        }
        rep(i,0,tot)v[i].clear();
        rep(i,0,25)trie[0][i]=1;
        q.push(1);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            rep(i,0,25){
                if(trie[u][i]){
                    fail[trie[u][i]]=trie[fail[u]][i];
                    q.push(trie[u][i]);
                }
                else{
                    trie[u][i]=trie[fail[u]][i];
                }
            }
        }
        rep(i,2,tot)v[fail[i]].push_back(i);
        dfs(1,0);
        memset(a,0,sizeof(a));
        rep(i,1,tot){
            if(num[i])continue;
            rep(j,0,25){
                int p=trie[i][j];
                if(num[p])continue;
                a[i][p]++;
            }
        }
        rep(i,1,tot){
            rep(j,1,tot)f[i][j]=a[i][j];
            f[i][i+tot]=a[i][i+tot]=1;
        }
        rep(i,tot+1,2*tot){
            rep(j,1,tot)f[i][j]=a[i][j]=0;
            f[i][i]=a[i][i]=1;
        }
        tot*=2;
        ll temp=26,anss=0;
        rep(i,1,n){
            anss+=temp;
            temp*=26;
        }
        int x=n;
        n--;
        for(;n;n>>=1){
            if(n&1)mul(f,a);
            mulself(a);
        }
        ll ans=0;
        rep(i,1,tot){
            ans+=f[1][i];
        }
        tot=2;
        f[1][1]=a[1][1]=26;
        f[1][2]=a[1][2]=1;
        f[2][1]=a[2][1]=0;
        f[2][2]=a[2][2]=1;
        n=x;
        n--;
        for(;n;n>>=1){
            if(n&1)mul(f,a);
            mulself(a);
        }
        anss=0;
        rep(i,1,tot)anss+=f[1][i];

        anss-=ans;
        cout<<anss<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值