1. 程式人生 > >【BZOJ1212】[HNOI2004]L語言

【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; 
}