【BZOJ1212】[HNOI2004]L语言

【BZOJ1212】[HNOI2004]L语言

题面

bzoj

洛谷

题解

其实可以不用AC自动机,但是最近在学就用了。。。

先把自动机建好,然后我们考虑怎么做。

设$vis[x]$表示文本串中$1-x$是否可以被表示出来

然后暴跳$fail$

设我们当前跳到了点$p$,在字符串的第$i$位

若有以$p$结尾的字符串且$vis[i-dep[p]]$,就证明$vis[i]$可以被拼出来了

好像做完了?然而并没有。

因为类似于$abcba$这样的串也会被判成由$abc$和$cba$构成的

所以我们要开一个差分数组看是否可以取到

体作可以看代码理解

代码

#include <iostream> 
#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <cmath> 
#include <algorithm>
#include <queue> 
using namespace std; 
const int MAX_N = 2e6;
const int MAX_M = 5000; 
struct AC_Auto {
	int c[MAX_M][26], fail[MAX_M], dep[MAX_M], cc[MAX_N], tot;
	bool vis[MAX_N], End[MAX_M]; 
	AC_Auto() : tot(0) { } 
	void insert(char *s) { 
		int o = 0; 
		for (int i = 1, l = strlen(s + 1); i <= l; i++) { 
			int son = s[i] - 'a'; 
			if (!c[o][son]) c[o][son] = ++tot; 
			o = c[o][son];
			dep[o] = i; 
		} 
		End[o] = 1; 
	} 
	void build() { 
		static queue<int> que; 
		for (int i = 0; i < 26; i++) if (c[0][i]) que.push(c[0][i]), fail[c[0][i]] = 0; 
		while (!que.empty()) { 
			int o = que.front(); que.pop(); 
			for (int i = 0; i < 26; i++)
				if (c[o][i]) fail[c[o][i]] = c[fail[o]][i], que.push(c[o][i]);
				else c[o][i] = c[fail[o]][i]; 
		} 
	} 
	int query(char *s) {
		memset(vis, 0, sizeof(vis)); 
		memset(cc, 0, sizeof(cc)); 
		vis[0] = 1;
		int l = strlen(s + 1); 
		for (int i = 1, o = 0; i <= l; i++) { 
			o = c[o][s[i] - 'a']; 
			for (int x = o; x; x = fail[x])
				if (End[x] && vis[i - dep[x]]) {
					vis[i] = 1; 
					cc[i - dep[x] + 1]++;
					cc[i + 1]--; 
				} 
		}
		int res = 0; 
		for (int i = 1, tot = 0; i <= l; i++) {
			tot += cc[i]; 
			if (tot) ++res; else break; 
		}
		return res; 
	} 
} ac;
int N, M; char s[MAX_N]; 
int main () {
	scanf("%d%d", &N, &M); 
	for (int i = 1; i <= N; i++) scanf("%s", s + 1), ac.insert(s); 
	ac.build(); 
	while (M--) {
		scanf("%s", s + 1);
		printf("%d\n", ac.query(s)); 
	} 
	return 0; 
} 
posted @ 2019-01-08 20:38  heyujun  阅读(112)  评论(0编辑  收藏  举报