POJ 寻找匹配
原题链接
题目描述
给定一个字符串S,寻找满足条件的子串数目,形式为ABA,A、B均为字符串,A的长度大于等于k,B的长度大于等于1.
输入
第一行为字符串S,第二行为整数k;
输出
一行一个整数,输出找到的满足条件的子串的数目
样例输入
aaaaa
1
样例输出
6
提示
字符均为小写字母,k<=100 S长度不超过15000
核心思路
注意到 A B A ABA ABA形式的字符串的特点就是公共前后缀,这与我们在KMP算法中求解的 N E X T NEXT NEXT数组的含义不谋而合(在不优化的情形下, N E X T NEXT NEXT数组的含义就是最长公共前后缀的长度)。于是我们的思路就是计算所有形如 s t r [ s : l e n − 1 ] : s ∈ [ 0 , l e n − 2 ] , l e n = s t r l e n ( s t r ) str[s:len-1]:s\in[0,len-2],len=strlen(str) str[s:len−1]:s∈[0,len−2],len=strlen(str)子串的 N E X T NEXT NEXT数组,在求解每个子串 N E X T NEXT NEXT数组的过程中统计 A B A ABA ABA子串的数量。
具体实现
- 如何迁移:正如上面所提到的, N E X T NEXT NEXT数组的含义是最长公共前后缀,与我们所要求的公共前后缀还略有距离。但如果熟悉 N E X T NEXT NEXT数组的本质的话,很容易想到,我们可以迭代的进一步考虑 N E X T [ N E X T [ i ] ] NEXT[NEXT[i]] NEXT[NEXT[i]]即最长公共前后缀的最长公共前后缀,如此不断迭代直至 N E X T [ i ] < = 0 NEXT[i]<=0 NEXT[i]<=0,我们便枚举完了位置 i i i的所有公共前后缀。
- 如何判断子串满足条件:需要满足两个条件, ( 1 ) : s t r l e n ( A ) > = k , ( 2 ) : s t r l e n ( B ) > = 1 (1):strlen(A)>=k,(2):strlen(B)>=1 (1):strlen(A)>=k,(2):strlen(B)>=1。我们知道 ( 3 ) N E X T [ i ] = s t r l e n ( A ) , ( 4 ) i = s t r l e n ( A B A ) (3)NEXT[i] =strlen(A),(4)i=strlen(ABA) (3)NEXT[i]=strlen(A),(4)i=strlen(ABA)。联立上式后便得到子串要满足的不等关系为 ( a ) N E X T [ i ] < = 2 ∗ i − 1 ( b ) N E X T [ i ] > = k (a)NEXT[i]<=2*i-1(b)NEXT[i]>=k (a)NEXT[i]<=2∗i−1(b)NEXT[i]>=k。
- 细节:题目所要求的是“子串的数量”。以 11211 11211 11211长度为5的子串为例,虽然有 1 ∣ 121 ∣ 1 1|121|1 1∣121∣1和 11 ∣ 2 ∣ 11 11|2|11 11∣2∣11两种分隔方式都满足 A B A ABA ABA的要求,但子串的数量只算作一个。因此在迭代寻找满足条件的 N E X T [ i ] NEXT[i] NEXT[i]时,只要找到满足条件的一个 N E X T [ i ] NEXT[i] NEXT[i],就立马停止迭代。
- 复杂度分析:求 N E X T NEXT NEXT数组复杂度为 O ( n ) O(n) O(n),从前到后遍历一遍 O ( n ) O(n) O(n),在迭代 N E X T [ N E X T [ i ] ] NEXT[NEXT[i]] NEXT[NEXT[i]]预计迭代次数不会太多,大致的复杂度在 O ( n 2 ) = 225000000 O(n^2)=225000000 O(n2)=225000000,能够通过本题十秒的限制。 (11月28日更新,经过同学提醒,算法的最差复杂度是 O ( n 3 ) O(n^3) O(n3),容易想到一组hack数据是 s t r = a ∗ 15000 , k = 1 str=a*15000,k=1 str=a∗15000,k=1。在这组数据下每次 N E X T [ N E X T [ i ] ] NEXT[NEXT[i]] NEXT[NEXT[i]]需要迭代 l e n / 2 len/2 len/2次才会才结束)
AC代码
#include<bits/stdc++.h>
using namespace std;
int K;
char s[15010];
short int NEXT[15010];
int GET_NEXT(char * s){
int len = strlen(s);
memset(NEXT, 0, sizeof(NEXT));
NEXT[0] = -1;
int i = 1, k = NEXT[i-1];
int cnt = 0;
while(i <= len){
while( k >=0 && s[i - 1] != s[k])
k = NEXT[k];
NEXT[i] = k + 1;
int temp = NEXT[i];
/* NEXT[NEXT[i]] till we find a qulified substr or NEXT[i] = -1 */
while(temp > 0){
if(temp >= K && 2 * temp < i){
cnt += 1;
break;
}
temp = NEXT[temp];
}
i += 1, k += 1;
}
return cnt;
}
int main(){
scanf("%s %d",s, &K);
int sum = 0;
int len = strlen(s);
for(int i = 0; i < len - 2;i += 1)
sum += GET_NEXT(s + i);
printf("%d\n", sum);
return 0;
}