2021“MINIEYE杯”中国大学生算法设计超级联赛(8)
hdu 7064 Singing Superstar
题意:
-
一个主串
n n n 个模式串
求主串当中有多少没有重合的匹配串
复盘:
-
第一感觉是 k m p kmp kmp,单个模式串 O ( n + m ) O(n+m) O(n+m) , n n n 个模式串就要 O ( n 2 ) O(n^2) O(n2) 了,会超时
又看了一遍输出的解释,发现 1 ≤ ∣ a i ∣ ≤ 30 1≤ |ai| ≤30 1≤∣ai∣≤30 ,模式串长度小于 30 30 30,这就可以进行操作了
枚举主串当中的所有长度小于 30 30 30 的子串,建一棵字典树
再在字典树上查询模式串即可
- 难想的一点:不能重合,这就很烦,详见代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int ans[N*30],tr[N*30][28],last[N*30];
void build(string s)
{
int len=s.size();
int tot=0;
memset(tr[0],0,sizeof(tr[0])); // 优化一下,加速了近500ms
for(int i=0;i<len;i++)
{
int now=0;
for(int j=0;j<30 && i+j<len; j++)
{
int c=s[i+j]-'a';
if(!tr[now][c])
{
tot++;
memset(tr[tot],0,sizeof(tr[tot]));
tr[now][c] = tot;
last[tot] = ans[tot]=0;
}
now=tr[now][c];
if(i==0 || i>last[now]) last[now]=i+j, ans[now]++;
// 若i<=last[now] 说明和前面的有重合
// 还是题目写太少了
}
}
}
char sr[N];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
string s;
cin>>s;
build(s);
int n;
scanf("%d",&n);
while(n--)
{
scanf("%s",sr);
int len=strlen(sr),now=0;
for(int i=0;i<len;i++)
{
now=tr[now][sr[i]-'a'];
if(now==0) break;
}
printf("%d\n",ans[now]);
}
}
return 0;
}