[2019寒假集训day2]子串(AC自动机+高斯消元)

题面

在这里插入图片描述
在这里插入图片描述

题解

首先显然是一个AC自动机上DP的题目。
接下来是套路:
dp[p]dp[p]dp[p]从AC自动机上的ppp点走到结束节点的期望步数。
这里的结束节点指AC自动机上插入的若干个串的最后一个字符所在节点。
显然对于结束节点dp[p]=0dp[p]=0dp[p]=0
对于其余节点,有转移方程dp[p]=(126∑i=126dp[ch[p][i]])+1dp[p]=(\frac{1}{26} \sum^{26}_{i=1} dp[ch[p][i]])+1dp[p]=(261i=126dp[ch[p][i]])+1
然而,我们知道,AC自动机实际上是一个图,而且也并不是DAG。
注意到本题的数据规模较小,考虑使用高斯消元暴力解方程。
方程列为:
dp[p]=0dp[p]=0dp[p]=0(ppp为结束节点)
26dp[p]−∑i=126dp[ch[p][i]]=2626dp[p]-\sum^{26}_{i=1} dp[ch[p][i]]=2626dp[p]i=126dp[ch[p][i]]=26(ppp为普通节点)
复杂度:O((n∣Si∣)3)O((n|S_i|)^3)O((nSi)3)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
#define MAXN 250
#define CHSIZ 26
#define MOD 1000000007
#define clr(x) memset(x,0,sizeof(x))
int T,n;
char s[MAXN+5];
int fun[MAXN+5][MAXN+5],ans[MAXN+5],is_free[MAXN+5];
int fst_pow(int a,int b)
{
	int res=1;
	while(b){
		if(b&1)res=(1LL*res*a)%MOD;
		a=(1LL*a*a)%MOD;
		b>>=1;
	}
	return res;
}
struct AC_automaton
{
	int tr[MAXN+5][CHSIZ+1],endp[MAXN+5];
	int fail[MAXN+5],pcnt;
	void Init(){pcnt=0;memset(tr[0],0,sizeof(tr[0]));}
	void Insert(char *s)
	{
		int len=strlen(s+1),p=0;
		for(int i=1;i<=len;i++){
			if(!tr[p][s[i]-'a'])
			{
				tr[p][s[i]-'a']=++pcnt;
				fail[pcnt]=0;endp[pcnt]=0;
				memset(tr[pcnt],0,sizeof(tr[pcnt]));
			}
			p=tr[p][s[i]-'a'];
		}
		endp[p]=1;
	}
	void Build()
	{
		queue<int> Q;
		for(int i=0;i<CHSIZ;i++)
		if(tr[0][i])Q.push(tr[0][i]);
		while(!Q.empty())
		{
			int p=Q.front();
			Q.pop();
			endp[p]|=endp[fail[p]];
			for(int i=0;i<CHSIZ;i++)
			if(tr[p][i])
			{
				fail[tr[p][i]]=tr[fail[p]][i];
				Q.push(tr[p][i]);
			}
			else tr[p][i]=tr[fail[p]][i];
		}
	}
	void make_fun()
	{
		clr(fun),clr(ans),clr(is_free);
		for(int i=0;i<=pcnt;i++)
		{
			if(endp[i])
				fun[i][i]=1,fun[i][pcnt+1]=0;
			else
			{
				fun[i][i]=26;
				for(int j=0;j<CHSIZ;j++)
				fun[i][tr[i][j]]--;
				fun[i][pcnt+1]=26;
			}
		}
	}
}ac;
int Gauss(int n,int m)
{
	int col=0,k=0;
	for(;k<m&&col<n;k++,col++)
	{
		int r=k;
		for(int i=k+1;i<m;i++)
		if(fabs(fun[r][col])<fabs(fun[i][col]))r=i;
		if(!fun[r][col]){is_free[col]=1;k--;continue;}
		if(r!=k)
		for(int i=col;i<=n;i++)
          swap(fun[k][i],fun[r][i]);
		for(int i=k+1;i<m;i++)
		if(fun[i][col])
		{
			int Div=(1LL*fun[i][col]*fst_pow(fun[k][col],MOD-2))%MOD;
			for(int j=col;j<=n;j++)
			fun[i][j]=(fun[i][j]-(1LL*fun[k][j]*Div)%MOD+MOD)%MOD;
			fun[i][col]=0;
		}
	}
	for(int i=k;i<m;i++)
    if(fun[i][n]) return -1;
	if(k<n)return n-k;
	for(int i=n-1;i>=0;i--)
    {
		int tmp=fun[i][n];
        for(int j=i+1;j<n;j++)
        tmp=(tmp-(1LL*ans[j]*fun[i][j])%MOD+MOD)%MOD;
        ans[i]=(1LL*tmp*fst_pow(fun[i][i],MOD-2))%MOD;
    }
	return 0;
}
int main()
{
	//freopen("substring.in","r",stdin);
	//freopen("substring.out","w",stdout);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		ac.Init();
		for(int i=1;i<=n;i++)
		{
			scanf("%s",s+1);
			ac.Insert(s);
		}
		ac.Build();
		ac.make_fun();
		Gauss(ac.pcnt+1,ac.pcnt+1);
		printf("%d\n",(ans[0]+MOD)%MOD);
	}
}

转载于:https://www.cnblogs.com/Panda-hu/p/11145738.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值