POJ 寻找匹配

POJ 寻找匹配

原题链接

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:len1]:s[0,len2],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]<=2i1(b)NEXT[i]>=k
  • 细节:题目所要求的是“子串的数量”。以 11211 11211 11211长度为5的子串为例,虽然有 1 ∣ 121 ∣ 1 1|121|1 11211 11 ∣ 2 ∣ 11 11|2|11 11211两种分隔方式都满足 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=a15000,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;
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值