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

本博客介绍了一种利用AC自动机和矩阵快速幂解决HDU 2243问题的方法,通过计算长度不超过n的非词根单词数,再从总单词数中减去这一部分来找出含有词根的单词数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思路:本题和POJ2778几乎是一样的,所以可以借鉴那一道题的思路。首先求出长度不超过n的不包含任何词根的单词数,然后用总的单词数减去这种情况即可。




#include<cstdio>
#include<cstring>
#include<string>
#include<cctype>
#include<iostream>
#include<set>
#include<map>
#include<cmath>
#include<sstream>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#define fin freopen("a.txt","r",stdin)
#define fout freopen("a.txt","w",stdout)
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int inf = 1e9 + 10;
const int maxnode = 35;
const int sigma_size = 26;
const int maxn = 35;
char T[maxn], s[4010];
char *p;

struct Matrix
{
    int r, c;
    ULL a[maxn][maxn];
    void init(int r, int c, ULL x) {
       this->r = r;
       this->c = c;
       for(int i = 1; i <= r; i++)
        for(int j = 1; j <= c; j++)
           a[i][j] = x;
    }

    
};

Matrix E;

Matrix operator + (const Matrix &A, const Matrix &B)
{
    int r = A.r, c = A.c;
    Matrix C; C.init(r, c, 0);
    for(int i = 1; i <= r; i++)
      for(int j = 1; j <= c; j++)
         C.a[i][j] = A.a[i][j] + B.a[i][j];
    return C;
}

Matrix operator * (const Matrix &A, const Matrix &B)
{
    int r = A.r, c = B.c;
    Matrix C; C.init(r, c, 0);
    for(int i = 1; i <= r; i++)
      for(int j = 1; j <= c; j++)
        for(int k = 1; k <= B.r; k++)
           C.a[i][j] = A.a[i][k] * B.a[k][j] + C.a[i][j];
    return C;
}

Matrix Pow(Matrix A, LL p)
{
    Matrix res = E;
    while(p)
    {
        if(p & 1) res = res * A;
        p >>= 1;
        A = A * A;
    }
    return res;
}

ULL Pow(ULL a, LL p)
{
	if(!p) return 1;
	ULL ans = Pow(a, p/2);
	ans *= ans;
	if(p & 1) ans *= a;
	return ans;
}

struct AC
{
    int ch[maxnode][sigma_size];
    int val[maxnode];
    int f[maxnode];
    int sz;
    int idx(char c) { return c - 'a'; }

    void init()
    {
        memset(ch[0], 0, sizeof ch[0]);
        sz = 1;
        memset(val,0,sizeof val);
    }

    void insert(char *s)
    {
        int n = strlen(s), u = 0;
        for(int i = 0; i < n; i++)
        {
            int c = idx(s[i]);
            if(!ch[u][c])
            {
                memset(ch[sz], 0, sizeof ch[sz]);
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
        }
        val[u] = 1;
    }

    int getFail()
    {
    	queue<int> q;
    	f[0] = 0;
    	for(int c = 0; c < sigma_size; c++)
    	{
    		int u = ch[0][c];
    		if(u) { f[u] = 0; q.push(u); }
    	}
    	while(!q.empty())
    	{
    		int r = q.front(); q.pop();
    		for(int c = 0; c < sigma_size; c++)
    		{
    			int u = ch[r][c];
    			if(!u) { ch[r][c] = ch[f[r]][c]; continue; }
    			q.push(u);
    			int v = f[r];
    			while(v && !ch[v][c]) v = f[v];
    			f[u] = ch[v][c];
    			val[u] |= val[f[u]];
    		}
    	}
    	return 0;
    }

    Matrix getMatrix()
    {
    	Matrix A;
        A.init(sz, sz, 0);
        E.init(sz, sz, 0);
        for(int i = 1; i <= sz; i++) E.a[i][i] = 1;
        for(int i = 0; i < sz; i++)
           for(int j = 0; j < sigma_size; j++)
           {
           	   int u = i, v = ch[i][j];
           	   if(!val[v]) ++A.a[u+1][v+1];
           }
         return A;
    }

}ac;


ULL Sigma(ULL a, LL p)
{
	if(p == 0) return 1;
	if(p == 1) return a;
	LL ans = Sigma(a, p/2);
	ans = (ULL(1) + Pow(a, p/2)) * ans;
	if(p & 1) ans += Pow(a, p);
	return ans;	
}


Matrix Sigma(Matrix A, LL p)
{
	if(p == 1) return A;
	if(p == 0) return E;
	Matrix Ans = Sigma(A, p/2);
	Ans = (E + Pow(A, p/2)) * Ans;
	if(p & 1) Ans = Ans + Pow(A, p);
	return Ans;
}

int main()
{
    int n; ULL l;
    char s[100];
    while(cin >> n >> l)
    {
    	ac.init();
    	for(int i = 1; i <= n; i++)
    	{
    		scanf("%s", s);
    		ac.insert(s);
    	}
    	ac.getFail();
    	Matrix A = ac.getMatrix();
    	A = Sigma(A, l);
    	ULL ans = Sigma(26, l);
    	for(int i = 1; i <= ac.sz; i++)
    	{
    		ans -= A.a[1][i];
    	}
    	cout << ans << endl;
    }
    
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值