LA3907 Puzzle

题意:给s个禁止子串,求不含他们的最长串。

思路:用这s个禁止串建一个 AC自动机,那么这些串的尾节点表示禁止节点。然后DFS判断是否存在环(为环就无限长了),在DFS的时候顺便记录下跑的路径,在输出字符串的时候就方便了。

代码:

#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
#define nn 100040

bool loop;//判断环
int road[nn];//记录路径
int vis[nn], dp[nn];//dp[i]表示以i节点结尾的字符串的长度
struct aho
{
	int ch[nn][26];
	int last[nn], val[nn], f[nn];
	int sz;
	void init()
	{
		sz = 1;
		memset(ch[0], 0, sizeof(ch[0]));
		val[0] = 0;
	}
	int idx(char c)
	{
		return c - 'A';
	}
	void insert(char* s, int m)
	{
		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;//标记所有不能走到的点
	}

	void get_f(int m)
	{
		queue<int>q;
		f[0] = 0;
		for (int c = 0; c<m; 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<m; 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];
				last[u] = val[f[u]] ? f[u] : last[f[u]];
			}
		}
	}
	int dfs(int now, int m)
	{
		if (loop) return 0;
		if (dp[now] != -1) return dp[now];
		dp[now] = 0;
		for (int i = m - 1; i >= 0; i--)
		{
			if (!val[ch[now][i]])
			{
				if (vis[ch[now][i]])//发现回路
				{
					loop = 1;
					return 0;
				}
				else
				{
					vis[ch[now][i]] = 1;
					if (dp[now]<1 + dfs(ch[now][i], m))
					{
						dp[now] = 1 + dp[ch[now][i]];
						road[now] = i; //记录路径,表示now节点选的下一个节点位置
					}
					vis[ch[now][i]] = 0;
				}
			}

		}
		return dp[now];
	}
};

aho t;
char str[100];
int main()
{
	int T, n, m;
	scanf("%d", &T);
	while (T--)
	{
		t.init();
		scanf("%d%d", &n, &m);
		for (int i = 0; i<m; i++)
		{
			scanf("%s", str);
			t.insert(str, n);
		}
		t.get_f(n);
		loop = 0;
		memset(vis, 0, sizeof(vis));
		memset(dp, -1, sizeof(dp));
		vis[0] = 1;
		t.dfs(0, n);
		if (loop || !dp[0]) printf("No\n");
		else
		{
			int now = 0;
			for (int i = 0; i<dp[0]; i++)
			{
				printf("%c", road[now] + 'A');
				now = t.ch[now][road[now]];
			}
			printf("\n");
		}
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值