【ybt高效进阶2-5-3】前缀匹配

前缀匹配

题目链接:ybt高效进阶2-5-3

题目大意

有一个大串和一些小串,问你每个小串的前缀在大串上的最长匹配长度是多少。
每个串只有 E,S,W,N 四个字符组成。

思路

由于我们看到字符串长度都很大,我们考虑不是把 AC 自动机和其他东西结合,而是把 AC 自动机进行变形。

那怎么变形呢?
我们可以枚举每个小串的每个前缀,然后询问是否在大串中出现,那就是要 O ( 1 ) O(1) O(1) 查询。

那怎么弄呢?我们考虑预处理出来。
那我们原本是查询每个小串是否在大串中出现,现在我们如果建出每个小串组成的 Trie 树,然后枚举大串的前缀,然后看在 Trie 树上的那些地方还有这个串(也就是跳 fail 边),那你找到的串都是大串的子串。

然后就好了。

代码

#include<queue>
#include<cstdio>
#include<cstring>

using namespace std;

struct Trie {
    
    
	int son[4], fail;
	bool in;
}tree[10000001];
int n, m, ans[100001], tot;
char s[10000001], c[100001][101];
queue <int> q;

int get_go(char c) {
    
    //给每个字符给一个独特的位置,减少空间
	if (c == 'E') return 0;
	if (c == 'S') return 1;
	if (c == 'W') return 2;
	if (c == 'N') return 3;
	return -1;//读入的时候这四个都不是,就要重新读入
}

void insert(int op) {
    
    //AC 自动机插入串
	int size = strlen(c[op]);
	int now = 0;
	for (int i = 0; i < size; i++) {
    
    
		int go = get_go(c[op][i]);
		if (!tree[now].son[go]) tree[now].son[go] = ++tot;
		now = tree[now].son[go];
	}
}

void build_fail() {
    
    //建 Trie 树上的 fail 边
	for (int i = 0; i <= 3; i++)
		if (tree[0].son[i]) {
    
    
			q.push(tree[0].son[i]);
			tree[tree[0].son[i]].fail = 0;
		}
	
	while (!q.empty()) {
    
    
		int now = q.front();
		q.pop();
		
		for (int i = 0; i <= 3; i++)
			if (tree[now].son[i]) {
    
    
				q.push(tree[now].son[i]);
				tree[tree[now].son[i]].fail = tree[tree[now].fail].son[i];
			}
			else tree[now].son[i] = tree[tree[now].fail].son[i];
	}
}

void add() {
    
    //给这个大串查询
	int now = 0;
	for (int i = 1; i <= n; i++) {
    
    //枚举它的前缀
		int go = get_go(s[i]);
		int noww = tree[now].son[go];
		while (noww) {
    
    //找到 Trie 树上所有的这个串
			tree[noww].in = 1;
			noww = tree[noww].fail;
		}
		now = tree[now].son[go];
	}
}

int ask(int op) {
    
    
	int size = strlen(c[op]);
	int now = 0, re = 0;
	for (int i = 0; i < size; i++) {
    
    //询问每个前缀是否存在于 Trie 数中
		int go = get_go(c[op][i]);
		now = tree[now].son[go];
		if (tree[now].in) re = i + 1;//存在,要找最长距离的
	}
	return re;
}

int main() {
    
    
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) {
    
    
		s[i] = getchar();
		while (get_go(s[i]) == -1) s[i] = getchar();
	}
	
	for (int i = 1; i <= m; i++) {
    
    
		scanf("%s", &c[i]);
		insert(i);
	}
	
	build_fail();
	
	add();
	
	for (int i = 1; i <= m; i++) {
    
    
		printf("%d\n", ask(i));
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/114415750