【ybt高效进阶2-3-4】子串拆分

子串拆分

题目链接:ybt高效进阶2-3-4

题目大意

问你在一个字符串中,有多少个 ABA 形的子串。
位置不同其他性质相同的子串算不同子串,位置相同但拆分不同的子串算同一子串。

思路

首先,这个范围可以适用于常数小的 n 2 n^2 n2 做法。

我们如果直接枚举两边范围,再看枚举 A 或者 B 的长度,就会超时。
可以想到看是否能组成 ABA 可以跑一下 KMP,然后看 f a i l n fail_n failn 的值,如果两个区间有相交的,就是不可以的。不过一个字符串可能有很多种搭配方法,而我们上面弄到的只是 A 最长的那一种,那我们要找到其他的,就是继续 i = f a i l i i=fail_i i=faili 直到变成 0 0 0,每次得到的都是可以的,只是会越来越短,那你只要一遇到不相交的就可以了。
至于什么时候不相交,如果字符串长度是 n n n,就要满足 2 × f a i l n < n 2\times fail_n< n 2×failn<n

但是我们最多只能接受 n 2 n^2 n2 的,而你这个是 n 3 n^3 n3
那我们考虑优化,我们发现我们只要确定左边界,跑完一整个 KMP 的时候,最大的那个会包含所有右边界的情况。

那我们就省去了枚举右边界的,然后就可以了。

代码

#include<cstdio>
#include<cstring>

using namespace std;

int K, n, fail[15001], nn, j, ans;
char s[15001], now[15001];

int main() {
    
    
	scanf("%s", s + 1);
	n = strlen(s + 1);
	scanf("%d", &K);
	
	for (int i = 1; i <= n; i++) {
    
    //枚举左边界
		memset(now, 0, sizeof(now));
		for (int k = 1; i + k - 1 <= n; k++)//构造出只有左边界的字符串
			now[k] = s[i + k - 1];
		memset(fail, 0, sizeof(fail));
		nn = strlen(now + 1);
		
		j = 0;
		for (int k = 2; k <= nn; k++) {
    
    //KMP
			while (j && now[k] != now[j + 1]) j = fail[j];
			if (now[k] == now[j + 1]) j++;
			fail[k] = j;
		}
		
		j = 0;
		for (int k = 1; k <= nn; k++) {
    
    
			while (j && now[k] != now[j + 1]) j = fail[j];
			if (now[k] == now[j + 1]) j++;
			
			while (j * 2 >= k) j = fail[j];//超过了一半(就是会重叠或中间没有)
			if (j >= K) ans++;//满足要求,长度大于等于K
		}
	}
	
	printf("%d", ans);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/112982673