社区讨论

求这道题 AC 自动机做法

P4421[COCI 2017/2018 #1] Lozinke参与者 1已保存回复 0

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
0 条
当前快照
1 份
快照标识符
@mhjstfmv
此快照首次捕获于
2025/11/04 07:56
4 个月前
此快照最后确认于
2025/11/04 07:56
4 个月前
查看原帖
根据 ACAM 的性质,如果我当前已经匹配到某个节点 pp 那么对于 pp 的任意一个祖先,以及 pp 的回退节点 qq 以及 qq 的任意一个祖先,以及 qq 的回退节点 kk ……都能够匹配,能不能利用这个性质解决这道题呢?
我的想法是对于每个节点,去看他的祖先和回退节点的祖先们有多少个节点是代表一个字符串的,于是在实现的时候出现了重复计数的问题,具体请看我的 dfs 函数(代码在最底下)。
样例 3 的图我画出来供参考
CPP
#include <iostream>
using namespace std;
const int N = 200010;
int idle = 1;
int tr[N][26] = { 0 };
char str[N][15];
int cnt[N] = { 0 };
int fa[N];
void insert(int i) {
    int p = 0;
    for (int j = 1; str[i][j]; j++) {
        int ch = str[i][j] - 'a';
        if (!tr[p][ch]) tr[p][ch] = idle++;
        fa[tr[p][ch]] = p;
		p = tr[p][ch];
    }
    cnt[p]++;    
}
int q[N], nex[N] = { 0 };
void build() {
	int hh = 1, tt = 1;
	for (int i = 0; i < 26; i++)
		if (tr[0][i])
			q[tt++] = tr[0][i];
	while (hh != tt) {
		int fat = q[hh++];
		for (int i = 0; i < 26; i++) {
			int j = tr[fat][i];
			if (j) nex[j] = tr[nex[fat]][i], q[tt++] = j;
			else tr[fat][i] = tr[nex[fat]][i];
		}
	}
}
bool mark[N] = { 0 };
/*计算每个节点能够到达的模式串数
 在样例 3 中,这种计数方式会重复计算,不知道如何修改 
 */
void dfs(int u) {
	if (mark[u]) return ;
	mark[u] = true;
	dfs(fa[u]);
	cnt[u] += cnt[fa[u]];
	dfs(nex[u]);
	cnt[u] += cnt[nex[u]];
} 
int get_cnt(int i) {
	int p = 0;
	for (int j = 1; str[i][j]; j++) {
		int ch = str[i][j] - 'a';
		p = tr[p][ch];
	} 
	return cnt[p] - 1;
}
#define ll long long
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%s", str[i] + 1);
        insert(i);
    }
    build();
    for (int i = 1; i < idle; i++) dfs(i);
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    	ans += get_cnt(i);
    printf("%lld", ans);
    return 0;
}

回复

0 条回复,欢迎继续交流。

正在加载回复...