BZOJ 3620 :似乎在梦中见过的样子
题目大意:给出一个长度为\(n\)的字符串和整数\(m\),求这个字符串有多少个子串满足ABA的形式,其中\(len(a) \geq m,len(b)\geq1\)
解析:
我们枚举子串的左端点,每次求出当前串的Fail数组,构造出KMP树,则对于每个右结点,我们要求的就是它的祖先结点中大于等于m的最小值,这个可以在构造Fail数组时顺便求出,总的时间复杂度为\(O(n^2)\)
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())
using namespace std;
typedef long long LL;
const int maxn=15000+23;
int n,m,size,L,R,cur,h[maxn],fail[maxn];
LL ans;
char a[maxn],s[maxn];
bool vis[maxn];
void get_fail()
{
cur=0;fail[1]=0;
rep(i,1,size-1)
{
while ((cur>0) && (a[i+1]!=a[cur+1])) cur=fail[cur];
if (a[i+1]==a[cur+1]) cur++;
fail[i+1]=cur;
if (fail[i+1]<m) {h[i+1]=0;continue;}
if ((h[fail[i+1]]>=m)) h[i+1]=h[fail[i+1]];
if ((h[fail[i+1]]==0)&&(fail[i+1]>=m)) h[i+1]=fail[i+1];
}
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
scanf("%d",&m);
if ((2*m+1)>n) {printf("%d\n",0);return 0;}
L=2*m+1,R=n-2*m,ans=0;
rep(i,1,R)
{
size=0;
rep(j,i,n) a[++size]=s[j];
get_fail();
rep(j,L,size) if ((h[j]>0)&&((h[j]<<1)<j)) ans++;
}
printf("%lld\n",ans);
return 0;
}