简单版
#include<bits/stdc++.h>
#define N 500010
using namespace std;
queue<int>q;
struct AC_{
// 字典树相关,分别表示: 存储图,标记单词尾,总节点个数
int c[N][26], val[N], cnt;
int fail[N]; // 失配转移
void ins(char *s){
int len=strlen(s);int now=0;
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!c[now][v])c[now][v]=++cnt;
now=c[now][v];
}
val[now]++; //标记 是一个单词尾
}
void build(){ // fail的建立, 注意fail建立之后 字典树中的指向已经变了
// 和根节点相连的加入队列中
for(int i=0;i<26;i++) if(c[0][i])fail[c[0][i]]=0,q.push(c[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++)
if(c[u][i]) fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);//如果u有i这个分支
else c[u][i]=c[fail[u]][i]; // 可以跳很多
}
}
int query(char *s){
int len=strlen(s);
int now=0,ans=0;
for(int i=0;i<len;i++){
now=c[now][s[i]-'a'];
// val[t] = -1 表示已经访问过, t = 0表示到了根节点
for(int t=now;t&&~val[t];t=fail[t])ans+=val[t],val[t]=-1;
}
return ans;
}
}AC;
int n;char p[1000005];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%s",p),AC.ins(p);
AC.build();
scanf("%s",p);int ans=AC.query(p);
printf("%d\n",ans);
return 0;
}
加强版 丑陋版,强行在字典树上找最大的字符串
代码
#include<bits/stdc++.h>
#define N 500010
using namespace std;
queue<int>q;
struct AC_{
// 字典树相关,分别表示: 存储图,标记单词尾,总节点个数
int c[N][26], val[N], cnt, a[N][26];
int fail[N]; // 失配转移
int id[N];
vector<int>ve;
void init(int n){
ve.clear();
memset(c[0], 0, sizeof(c[0]) * (n + 1));
memset(a[0], 0, sizeof(a[0]) * (n + 1));
memset(val, 0, sizeof(int) * (n + 1));
memset(fail, 0, sizeof(int) * (n + 1));
cnt = 0;
}
void ins(char *s, int n){
int len=strlen(s);int now=0;
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!c[now][v]) c[now][v]=++cnt;
if(!a[now][v]) a[now][v] = cnt;
now=c[now][v];
}
id[now] = n;
val[now]++; //标记 是一个单词尾
}
void build(){ // fail的建立
// 和根节点相连的加入队列中
for(int i=0;i<26;i++) if(c[0][i])fail[c[0][i]]=0,q.push(c[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++)
if(c[u][i]) fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);//如果u有i这个分支
else c[u][i]=c[fail[u]][i]; // 可以跳很多,同时也方便匹配
}
}
int query(char *s){
int len=strlen(s);
int now=0,ans=0;
for(int i=0;i<len;i++){
now=c[now][s[i]-'a'];
// val[t] = -1 表示已经访问过, t = 0表示到了根节点
for(int t=now;t&&~val[t];t=fail[t]) {
if(val[t]) ans = max(ans, ++val[t]);
}
}
return ans;
}
void dfs(int now, int mx){ // 因为我想遍历原字典树,但是经过fail之后c已经变了,故又开了一个a
if(mx == val[now]) ve.push_back(now);
for(int i = 0 ;i < 26; i++){
if(a[now][i]) {
dfs(a[now][i], mx);
}
}
}
void print(){ // 又发现,这里排序有问题,不能够直接按照 节点标号排序,应该按照id[ve[i]]的由小到大排序,
//我这里是有问题的,但不知道为什么可以过,懒的改了.
sort(ve.begin(), ve.end());
}
}AC;
int n;char p[1000005];
char ss[200][71];
int main(){
while(scanf("%d",&n) && n){
for(int i=1;i<=n;i++)scanf("%s",ss + i),AC.ins( ss[i], i);
AC.build();
scanf("%s", p);
int ans = AC.query(p); // 获取模板出现的最大值
printf("%d\n", ans - 1);
AC.dfs(0, ans); //dfs遍历字典树,找到所有最大值的串
AC.print();
for(int i = 0; i < (int)AC.ve.size(); i++){
printf("%s\n", ss[AC.id[AC.ve[i]]]);
}
AC.init(AC.cnt + 1);
}
return 0;
}
优雅的加强版代码
#include<bits/stdc++.h>
#define N 500010
using namespace std;
queue<int>q;
struct AC_{
// 字典树相关,分别表示: 存储图,标记单词尾,总节点个数
int c[N][26], val[N], cnt, a[N][26];
int fail[N]; // 失配转移
int id[N];
int ans[160];
void init(int n){
memset(c[0], 0, sizeof(c[0]) * (n + 1));
memset(a[0], 0, sizeof(a[0]) * (n + 1));
memset(val, 0, sizeof(int) * (n + 1));
memset(fail, 0, sizeof(int) * (n + 1));
memset(ans, 0, sizeof(ans));
cnt = 0;
}
void ins(char *s, int n){
int len=strlen(s);int now=0;
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!c[now][v]) c[now][v]=++cnt;
if(!a[now][v]) a[now][v] = cnt;
now=c[now][v];
}
id[now] = n;
val[now]++; //标记 是一个单词尾
}
void build(){ // fail的建立
// 和根节点相连的加入队列中
for(int i=0;i<26;i++) if(c[0][i])fail[c[0][i]]=0,q.push(c[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++)
if(c[u][i]) fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);//如果u有i这个分支
else c[u][i]=c[fail[u]][i]; // 可以跳很多,同时也方便匹配
}
}
int query_mx(char *s){
int len=strlen(s);
int now=0,res=0;
for(int i=0;i<len;i++){
now=c[now][s[i]-'a'];
// val[t] = -1 表示已经访问过, t = 0表示到了根节点
for(int t=now;t&&~val[t];t=fail[t]) {
if(val[t]) {
res = max(res, ++ans[id[t]]);
}
}
}
return res;
}
}AC;
int n;char p[1000005];
char ss[200][71];
int main(){
while(scanf("%d",&n) && n){
for(int i=1;i<=n;i++)scanf("%s",ss + i),AC.ins( ss[i], i);
AC.build();
scanf("%s", p);
int ans = AC.query_mx(p); // 获取模板出现的最大值
printf("%d\n", ans);
for(int i = 1; i <= n; i++) if(AC.ans[i] == ans) printf("%s\n", ss[i]);
AC.init(AC.cnt + 1);
}
return 0;
}