UVA12244 Growing Strings

UVA12244 Growing Strings

题面

形式化题意

给定 \(N\) 个字符串 \(S_1 \dots S_N\),从中选出一些字符串首尾相接依次排成一个序列。在序列中,前面串是后面串的子串。
每组数据输出一行一个整数,表示最多可以选择的字符串个数。

思路

对于字符串匹配问题,第一时间想到 AC 自动机

AC 自动机利用失配 Fail 指针来辅助多模式串的匹配。
Fail 指针指向所有模式串的前缀中匹配当前状态的最长后缀

不难发现 Fail 指针与我们本题字符串匹配模式十分相似。
那么本题的解法就很容易想到了。

AC 自动机+DP

\(dp[i]\) 表示以节点 \(i\) 结尾时最多可以选多少个字符串。
需知道以其父亲节点\(Fail\) 节点为结尾的可选字符串数量,再加上以 \(i\) 为结尾的字符串数量
\(dp[i]= \max(dp[i\_father],dp[i\_fail])+num[i]\)

样例解释:

对于样例一:

我们只要在构建 Fail 树的同时进行 DP,就可以找到最多可选字符串的数量了。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T&x){//快读 
	int w=0;x=0;
	char ch = getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') w=1;
		ch = getchar();
	}
	while(ch>='0'&&ch<='9'){
		x = (x<<1)+(x<<3)+(ch^48);
		ch = getchar();
	}
	if(w) x=-x;
}
template <typename T,typename...Args>
inline void read(T&t,Args&...args){
	read(t);read(args...);
}
inline int max(int a,int b){
  return (a > b ? a : b);
}
#define mian main

const int N = 1e6+5;
int n;
char s[N];
struct TRIE{
	int son[27],fail,num;//宏定义qwq 
	#define son(x,y) Trie[x].son[y]
	#define fail(x) Trie[x].fail
	#define num(x) Trie[x].num
//	TRIE(){
//		memset(son,0,sizeof(son));
//		fail = pos = fu = ind = num = 0;
//	}
}Trie[N];
int dp[N],rt=0,cnt;
inline void insert(char *s){//建Trie树 
	int now = rt;
	int len = strlen(s);
	for(int i=0;i<len;++i){
		int c = s[i]-'a';
		if(!son(now,c)) son(now,c) =  ++cnt;
		now = son(now,c);
	}
	++num(now);
}
queue <int> q;
inline void get_fail(){//建Fail树 
	int res = 0;
	q.push(0);
	while(q.size()){
		int now = q.front();
		q.pop();
		int tfail = fail(now);
		if(num(tfail)) num(now) = 1;
		for(int i=0;i<26;++i){
			int tson = son(now,i);
			if(tson) q.push(tson);
			else son(now,i) = son(tfail,i);
			if(now) fail(tson) = son(tfail,i);//Root==0根 
			dp[tson] = max(dp[now],dp[fail(tson)])+num(tson);//建树同时更新节点信息 
			res = max(res,dp[tson]);
		}	
	}
	printf("%d\n",res);
}
inline void Init(){
	memset(Trie,0,sizeof(TRIE)*(cnt+1));
	memset(dp,0,sizeof(dp));
	cnt = 0;
}
int main(){
	while(1){
		read(n);
		if(!n) break;
		Init();//多测清空
		while(n--){
			scanf("%s",s);
			insert(s);
		}
		get_fail();
	}
	return 0;
}
posted @ 2025-03-09 10:13  Tmbcan  阅读(49)  评论(0)    收藏  举报