hdu 6086 Rikka with String ac自动机+dp

22 篇文章 0 订阅
1 篇文章 0 订阅


Rikka with String

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 210    Accepted Submission(s): 58


Problem Description
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.

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 4
 

Source
 

Recommend
liuyiding   |   We have carefully selected several similar problems for you:   6095  6094  6093  6092  6091 
 

Statistic |  Submit |  Discuss |  Note
题意:给你n个串,长度L,问你能构造出多少个字符串长度为2L满足包含这n个串以及他的反对称串。

思路:这题好恶心。。。调了大半天才调出来。很明显是ac自动机+dp。我们可以这么想,我们构造一个L的串,然后把他反对称就好了。那么前面包含的串它的反对称串也必定包含。所以构造ac自动机的时候把这n个串和他们的反对称串都插进去然后结尾标记为同一个状态。还有一个情况就是串出现的地方跨过了中间。例如样例中的011和001,若00出现在我们构造的串的最后,那么根据反对称中间会出现0011,也就是包含了011和001,所以我们把串拆开。把出现在结尾也可改变状态的串插入自动机。可能说明有点复杂,具体描述看官方博客的题解吧。下面给代码:

#include <bits/stdc++.h>

using namespace std;
#define maxn 5005
const int mod = 998244353;
struct Trie
{
	int next[maxn][2], fail[maxn], end1[maxn], end2[maxn], mark[maxn];
	int root, L;
	int newnode()
	{
		for (int i = 0; i<2; i++)
			next[L][i] = -1;
		end1[L] = 0;
		end2[L++] = 0;
		return L - 1;
	}
	void init()
	{
		memset(mark, -1, sizeof(mark));
		L  = 0;
		root = newnode();
	}
	void insert1(char buf[],int &id)
	{
		int len = strlen(buf);
		int now = root;
		for (int i = 0; i<len; i++)
		{
			if (next[now][buf[i] - '0'] == -1)
				next[now][buf[i] - '0'] = newnode();
			now = next[now][buf[i] - '0'];
		}
		if (mark[now]>=0){
			id=mark[now];
			return;
		}
		mark[now] = id;
		end1[now] |= 1 << mark[now];
	}
	void insert2(char buf[],int &id)
	{
		int len = strlen(buf);
		int now = root;
		for (int i = 0; i<len; i++)
		{
			if (next[now][buf[i] - '0'] == -1)
				next[now][buf[i] - '0'] = newnode();
			now = next[now][buf[i] - '0'];
		}
		end2[now] |= 1 << id;
	}
	void build()
	{
		queue<int>Q;
		fail[root] = root;
		for (int i = 0; i<2; i++)
		{
			if (next[root][i] == -1)
				next[root][i] = root;
			else
			{
				fail[next[root][i]] = root;
				Q.push(next[root][i]);
			}
		}
		while (!Q.empty())
		{
			int now = Q.front();
			Q.pop();
			end1[now] |= end1[fail[now]];
			end2[now] |= end2[fail[now]];
			for (int i = 0; i<2; i++)
			{
				if (next[now][i] == -1)
					next[now][i] = next[fail[now]][i];
				else
				{
					fail[next[now][i]] = next[fail[now]][i];
					Q.push(next[now][i]);
				}
			}
		}
	}
};
Trie ac;
const int maxlen=100;
void getsub(char *s,int id)
{
	int len=strlen(s);
	char ss[maxlen];
	strcpy(ss,s);
	ss[len]='\0';
	for(int i=1;i<len;i++)
	{
		int length=len-i+1;
		bool flag=true;
		bool leftover=false;
		bool rightover=false;
		for(int j=1;j<=length;j++)
		{
			if(i-j<0) 
			{
				leftover=true;
				break;
			}
			if(ss[i+j-1]==ss[i-j]) 
			{
				flag=false;
				break;
			}
		}
		if(flag)
		{
			char str[25];
			int now=0;
			if(leftover)
			{
				for(int j=len-1;j>=i;j--) str[now++]=!(ss[j]-'0')+'0';
			}
			else
			{
				for(int j=0;j<i;j++) str[now++]=(ss[j]-'0')+'0';
			}
			str[now]='\0';
			ac.insert2(str,id);
		}
		
	}
}
int dp[2][maxn][(1 << 6) + 5];
int main()
{
	int t;
	scanf("%d", &t);
	char buf[10][25];
	char s[25];
	while (t--){
		memset(dp, 0, sizeof(dp));
		int n, l;
		ac.init();
		int id=0;
		scanf("%d%d", &n, &l);
		for (int i = 0; i < n; i++){
			scanf("%s", buf[i]);
			int now=id;
			ac.insert1(buf[i],now);
			getsub(buf[i],now);
			strcpy(s,buf[i]);
			strrev(s);
			for(int j=0;s[j]!='\0';j++)
				s[j]=(!(s[j]-'0'))+'0';
			ac.insert1(s,now);
			if(now==id)
				id++;
		}
		ac.build();
		dp[0][0][0] = 1;
		int now=0;
		for (int i = 1; i <= l; i++){
			now^=1;
			memset(dp[now],0,sizeof(dp[now]));
			for (int j = 0; j < ac.L; j++)
				for (int k = 0; k < 1 << id; k++)
					for (int ll = 0; ll < 2; ll++){
						int Next = ac.next[j][ll];
						int nextstatus = k | ac.end1[Next];
						if (i == l)
							nextstatus |= ac.end2[Next];
						dp[now][Next][nextstatus] += dp[now^1][j][k];
						dp[now][Next][nextstatus] %= mod;
					}
		}
		int ans = 0;
		for (int i = 0; i < ac.L; i++){
			ans += dp[now][i][(1 << id) - 1];
			ans %= mod;
		}
		printf("%d\n", ans);
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值