Hdu-6086 Rikka with String(AC自動機+DP)

As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:

Yuta has n01 strings si, and he wants to know the number of 01 antisymmetric strings of length 2L which contain all given strings si as continuous substrings.

A 01 string s is antisymmetric if and only if s[i]s[|s|i+1] for all i[1,|s|].

It is too difficult for Rikka. Can you help her?

In the second sample, the strings which satisfy all the restrictions are 000111,001011,011001,100110



.
Input The first line contains a number t(1t5), the number of the testcases.

For each testcase, the first line contains two numbers n,L(1n6,1L100).

Then n lines follow, each line contains a 01 string si(1|si|20). Output For each testcase, print a single line with a single number -- the answer modulo 998244353.Sample Input
2
2 2
011
001
2 3
011
001
Sample Output
1

分析:現在要求的是包含所有模板的情況,因爲n很小所以我們可以容斥做,每次枚舉一個子集然後求不包含這個子集的任何一個模板的情況,把模板全部翻轉後再插入就可以
表示後綴了,dp[i][j][k]表示當前匹配到第i位其中前綴匹配到自動機的第j個點,後綴匹配到第k個點的方案數,最後枚舉一下dp[L][j][k]中合法的j,k再統計答案就可了,這一步可以
暴力沿着fail指針遍歷判斷前綴後綴會不會拼出新的模板.

#include<bits/stdc++.h>
using namespace std;
const int MOD = 998244353;
const int MAXNODE = 125;
int T,n,l,ans,rc[1<<6],dp[105][125][125];
char s[7][25],rs[7][25];
struct ACautomata
{
	int ch[MAXNODE][2];
	int num[MAXNODE];
	int deep[MAXNODE];
	int f[MAXNODE];             // fail函數
	int val[MAXNODE]; 	        // 是否爲單詞結尾 
	int tot;                    // trie 單詞總數 
	int last[MAXNODE]; 
	void init()
	{
		tot = 1;
		memset(ch,0,sizeof(ch));
		memset(num,0,sizeof(num));
		memset(last,0,sizeof(last));
		memset(f,0,sizeof(f));
	}
	int idx(char c)
	{
		return c - '0';
	}
	void insert(char *s,int v)
	{
		int u = 0,n = strlen(s);
		for(int i = 0;i < n;i++)
		{
			int c = idx(s[i]);
			if(!ch[u][c])
			{
				val[tot] = 0;
				deep[tot] = deep[u] + 1;
				ch[u][c] = tot++; 
			}
			num[u] |=  1<<(v-1);
			u = ch[u][c];
		}
		val[u] = true;
		num[u] |= 1<<(v-1);
	}
	void getFail()
	{
		queue<int> q;
		f[0] = 0;
		for(int c = 0;c < 2;c++)
		{
			int u = ch[0][c];
			if(u)
			{
				f[u] = 0;
				q.push(u);
				last[u] = 0;
			}
		}
		while(!q.empty())
		{
			int r = q.front();q.pop();
			for(int c = 0;c < 2;c++)
			{
				int u = ch[r][c];
				if(!u)
				{
					ch[r][c] = ch[f[r]][c];
					continue;
				}
				q.push(u);
				f[u] = ch[f[r]][c];
				last[u] = val[f[u]] ? f[u] : last[f[u]];
			}
		}
	}
}tree1,tree2;
bool check(int x,int y)
{
	if(tree1.val[x] || tree2.val[y] || tree1.last[x] || tree2.last[y]) return false;
	for(int i = 1;i <= n;i++)
	{
		int len = strlen(s[i]);
		for(int u = x;u;u = tree1.f[u])
		 if(tree1.num[u] & (1<<(i-1)))
		  for(int v = y;v;v = tree2.f[v])
		   if(tree2.num[v] & (1<<(i-1)))
		   {
			  if(tree1.deep[u] + tree2.deep[v] < len) break;
			  if(tree1.deep[u] + tree2.deep[v] == len) return false;
		   } 
	}
	return true;
}
int main()
{
	cin.sync_with_stdio(false);
	cin>>T;
	while(T--)
	{
		ans = 0;
		cin>>n>>l;
		for(int i = 1;i <= n;i++) 
		{
			cin>>s[i];
			int m = strlen(s[i]);
			for(int j = 0;j < m;j++) rs[i][j] = s[i][m-1-j];
			rs[i][m] = s[i][m];
		}
		rc[0] = 1;
		for(int i = 1;i < (1<<n);i++) rc[i] = rc[i - (i & -i)] * -1;
		for(int mask = 0;mask < (1<<n);mask++)
		{
			tree1.init(),tree2.init();
			memset(dp,0,sizeof(dp));
			for(int i = 1;i <= n;i++)
			 if(mask & (1<<(i-1))) tree1.insert(s[i],i),tree2.insert(rs[i],i);
			tree1.getFail();
			tree2.getFail();
			dp[0][0][0] = 1;
			for(int i = 0;i < l;i++)
			 for(int j = 0;j < tree1.tot;j++)
			  for(int k = 0;k < tree2.tot;k++)
			   if(dp[i][j][k] && !tree1.val[j] && !tree2.val[k] && !tree1.last[j] && !tree2.last[k])
				for(int c = 0;c < 2;c++)
				{
					int next1 = tree1.ch[j][c],next2 = tree2.ch[k][c^1];
					dp[i+1][next1][next2] += dp[i][j][k];
					dp[i+1][next1][next2] %= MOD;
				}
			for(int j = 0;j < tree1.tot;j++)
			 for(int k = 0;k < tree2.tot;k++)
			  if(dp[l][j][k] && check(j,k)) 
			  {
				  ans += (MOD + dp[l][j][k]*rc[mask] % MOD) % MOD,ans %= MOD;
				  if(ans < 0) cout<<233<<endl;
			  }
		}
		cout<<ans<<endl;
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章