[BZOJ]4327 [JSOI2012] 玄武密码 AC自动机

4327: JSOI2012 玄武密码

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 435   Solved: 194
[ Submit][ Status][ Discuss]

Description

在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河。相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中。老人们说,这是玄武神灵将天书藏匿在此。 
很多年后,人们终于在进香河地区发现了带有玄武密码的文字。更加神奇的是,这份带有玄武密码的文字,与玄武湖南岸台城的结构有微妙的关联。于是,漫长的破译工作开始了。 
经过分析,我们可以用东南西北四个方向来描述台城城砖的摆放,不妨用一个长度为N的序列来描述,序列中的元素分别是‘E’,‘S’,‘W’,‘N’,代表了东南西北四向,我们称之为母串。而神秘的玄武密码是由四象的图案描述而成的M段文字。这里的四象,分别是东之青龙,西之白虎,南之朱雀,北之玄武,对东南西北四向相对应。 
现在,考古工作者遇到了一个难题。对于每一段文字,其前缀在母串上的最大匹配长度是多少呢? 

Input

第一行有两个整数,N和M,分别表示母串的长度和文字段的个数。 
第二行是一个长度为N的字符串,所有字符都满足是E,S,W和N中的一个。 
之后M行,每行有一个字符串,描述了一段带有玄武密码的文字。依然满足,所有字符都满足是E,S,W和N中的一个。 

Output

输出有M行,对应M段文字。 
每一行输出一个数,表示这一段文字的前缀与母串的最大匹配串长度。 

Sample Input

7 3
SNNSSNS
NNSS
NNN
WSEE

Sample Output

4
2
0

HINT

对于100%的数据,N<=10^7,M<=10^5,每一段文字的长度<=100。


应上传者要求,此题不公开,如有异议,请提出.


Source

[ Submit][ Status][ Discuss]


HOME Back

  直接建AC自动机然后把母串拿上去匹配打vis标记即可.然后一个字符串的最长匹配前缀就是最深被vis的点.

  要把vis标记向fail传递.于是乎要用到基数排序来搞定拓扑关系.

#include<bits/stdc++.h>
using namespace std;
const int maxm = 1e7 + 5;
const int maxn = 1e5 + 5;
int n, m, tot, top;
bool vis[maxm];
char s[maxn][105], str[maxm];
int c[maxm][5], fail[maxm], mean[320], len[maxm], sa[maxm], cnt[maxm];
inline void insert(char* ss) {
	int p = 0;
	for (int i = 0; ss[i]; ++ i) {
		int idx = mean[(int)ss[i]];
		if (!c[p][idx]) c[p][idx] = ++ tot;
		p = c[p][idx], len[p] = i + 1;
	}
}
queue<int> q;
inline void bfs() {
	for (int i = 0; i < 4; ++ i)
		if (c[0][i]) q.push(c[0][i]);
	while (!q.empty()) {
		int u = q.front(); q.pop();
		for (int i = 0; i < 4; ++ i) {
			int v = c[u][i];
			if (!v) {c[u][i] = c[fail[u]][i]; continue;}
			fail[v] = c[fail[u]][i];
			q.push(v);
		}
	}
}
inline void work() {
	int p = 0;
	register int i;
	for (i = 0; str[i]; ++ i) {
		int idx = mean[(int)str[i]];
		p = c[p][idx], vis[p] = true;
	}
	for (i = 1; i <= tot; ++ i) cnt[len[i]] ++;
	for (i = 1; i <= tot; ++ i) cnt[i] += cnt[i - 1];
	for (i = 1; i <= tot; ++ i) sa[cnt[len[i]] --] = i;
	for (i = tot; i; -- i) vis[fail[sa[i]]] |= vis[sa[i]];
}
inline void query(char* ss) {
	int p = 0, mx = 0;
	for (int i = 0; ss[i]; ++ i) {
		int idx = mean[(int)ss[i]];
		p = c[p][idx];
		if (vis[p]) mx = len[p];
		else break;
	}
	printf("%d\n", mx);
}
int main() {
	mean['E'] = 0, mean['S'] = 1, mean['W'] = 2, mean['N'] = 3;
	scanf("%d%d", &n, &m);
	scanf("%s", str);
	for (register int i = 0; i < m; ++ i) {
		scanf("%s", s[i]);
		insert(s[i]);
	}
	bfs(), work();
	for (register int i = 0; i < m; ++ i) query(s[i]);
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值