hdu2243 考研路茫茫――单词情结 [kuangbin专题-ac自动机]

在做这题前建议先做下poj2778
poj2778
本题跟poj2778有点类似,他是求不含模式串,长度为n的字符串的个数,本题是求至少包含一个,那么很简单,求出总数,再将不含模式串的数量减去即可。

有一些细节需要注意,首先本题的mod是2^64,所以我们把数据开ull即可。然后求总数的话也需要借助矩阵快速幂,线性递推的时间复杂度也是不可接受的。另外,本题还涉及到求和,所以状态转移矩阵中需要增加一维,第L+1列全部为1。

#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <queue>
#include <map>
#include <cstring>
#define fi first
#define se second
#define FIN freopen("in.txt","r",stdin)
#define FIO freopen("out.txt","w",stdout)
#define INF 0x3f3f3f3f
#define per(i,a,n) for(int i = a;i < n;i++)
#define rep(i,a,n) for(int i = n;i > a;i--)
#define pern(i,a,n) for(int i = a;i <= n;i++)
#define repn(i,a,n) for(int i = n;i >= a;i--)
#define fastio std::ios::sync_with_stdio(false)
#define all(a) a.begin(), a.end()
#define ll unsigned long long
#define pb push_back
#define endl "\n"
#define pii pair<int,int>
#define sc(n) scanf("%d", &n)
#define CASET int ___T; scanf("%d", &___T); for(int cs=1;cs<=___T;cs++)
template<typename T> inline void _max(T &a,const T b){if(a<b) a = b;}
template<typename T> inline void _min(T &a,const T b){if(a>b) a = b;}
using namespace std;
//inline ll read(){
//    ll a=0;int f=0;char p=getchar();
//    while(!isdigit(p)){f|=p=='-';p=getchar();}
//    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();}
//    return f?-a:a;
//}
const int maxn = 10000*50;
//const int mod = 100000;
const int Size = 110;

struct Marix{
    ll a[Size][Size];
    ll n;
    Marix(){
        n = Size;
        memset(a, 0, sizeof(a));
    }
    Marix(ll _n)
    {
        n = _n;
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
                a[i][j] = 0;
        }
    }
    Marix operator * (const Marix &A){
        Marix B = Marix(A.n);
        for(int k = 0; k < A.n; ++k){
            for(int i = 0; i < A.n; ++i){
                for(int j = 0; j < A.n; ++j){
                    B.a[i][j] = (B.a[i][j] + (a[i][k]*A.a[k][j] ) ) ;
                }
            }
        }
        return B;
    }
};
Marix mul(Marix a,Marix b){//矩阵乘法
    Marix res=Marix(a.n);
    for(int i=0;i<a.n;i++){
        for(int j=0;j<a.n;j++){
            for(int k=0;k<a.n;k++){
                ll tmp=a.a[i][k]*b.a[k][j];
                res.a[i][j]=(res.a[i][j]+tmp);
            }
        }
    }
    return res;
}

Marix powMod(Marix A, ll cnt){
    Marix res = Marix(A.n);
    for(int i = 0; i < A.n; ++i)res.a[i][i] = 1;
    while(cnt){
        if(cnt&1)res = mul(res,A);
        //cout << res.a[0][1] << endl;
        A = mul(A,A);
        cnt >>= 1;
    }
    return res;
}


const int maxnode = 26;
int ch[maxn][maxnode]; //字典树
int cnt[maxn];    //单词出现次数
int sz;
int fail[maxn];
//map<char,int> mp;
void init()
{
    //mp['A'] = 0,mp['C']=1,mp['T']=2,mp['G']=3;
    sz = 1;
    memset(ch[0], 0, sizeof(ch[0]));
    memset(cnt,0,sizeof(cnt));
    //val[0] = 0;
    cnt[0] = 0;
}
void insert(char str[], int len) //插入字符串
{
    int u = 0;
    per(i, 0, len)
    {
        int v = str[i]-'a';
        if (!ch[u][v])
        {
            memset(ch[sz], 0, sizeof(ch[sz]));
            //val[sz] = 0;
            cnt[sz] = 0;
            ch[u][v] = sz++;
        }
        u = ch[u][v];
    }
    cnt[u]=1;
    //在这里我们可以建立一个int-string的映射,以通过节点序号得知这个点是哪个单词的结尾
}

void getfail()
{
    //所有模式串已插入完成
    queue<int> q;
    per(i, 0, maxnode)
    {
        if (ch[0][i])
        {
            fail[ch[0][i]] = 0;
            q.push(ch[0][i]);
        }
    }
    while (!q.empty())
    {
        int now = q.front();
        q.pop();
        per(i, 0, maxnode)
        {
            if (ch[now][i])
            {
                fail[ch[now][i]] = ch[fail[now]][i];
                q.push(ch[now][i]);
            }
            else
                ch[now][i] = ch[fail[now]][i];
        }
        cnt[now] |= cnt[fail[now]];
    }
}

Marix getmatrix()
{
    Marix ma = Marix(sz+1);
    per(i,0,sz)
    {
        per(j,0,maxnode)
        {
            if(cnt[ch[i][j]]==1)continue;
            ma.a[i][ch[i][j]]++;
        }
    }
    for(int i=0;i<sz+1;i++){//再多开一维,使得第id+1列全都置为1
        ma.a[i][sz]=1;
    }
    return ma;
}
char s[20];
int main()
{

    #ifndef ONLINE_JUDGE
        int startTime = clock();
        FIN;
    #endif
    //fastio;
	//忘记初始化是小狗
    //freopen("out.txt","w",stdout);
    //ios::sync_with_stdio(false);
    int n,m;
    //cout << id('0');
    while(~scanf("%d%d",&n,&m)){
        init();
        per(i,0,n)
        {
            scanf("%s",s);
            insert(s,strlen(s));
        }
        getfail();
        Marix mm = getmatrix();
//        per(i,0,sz)
//        {
//            per(j,0,sz)cout << mm.a[i][j] << ' ';
//            cout << endl;
//        }
        Marix r1 = powMod(mm,m);
        ll res = 0,ans = 0;
        per(i,0,sz+1)res = (res+r1.a[0][i]);
        res--;
        Marix r2 = Marix(2);
        r2.a[0][0] = 26,r2.a[0][1] = r2.a[1][1] = 1;
        Marix r3 = powMod(r2,m);
        ans = r3.a[0][0]+r3.a[0][1];
        ans--;
        cout << ans-res << endl;
        //cout << ans-1 << endl;
    }
    #ifndef ONLINE_JUDGE
        printf("\nTime = %dms\n", clock() - startTime);
    #endif
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值