Hdu-6086 Rikka with String(AC自动机+DP)

185 篇文章 0 订阅
96 篇文章 0 订阅
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 n 01 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;
	}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值