蓝桥杯 ALGO-133 Tricky and Clever Password

题意:题目的意思已经描述的很详细了,这里只作简要的说明,就是存在这样的一个字符串,A+pre+B+middle+C+suffix,我们要求的就是pre+middle+suffix的最大值。

思路:最主要的思路就是枚举中缀的所有情况,用malacher算法求出以该节点的半径,那么middle就出来了,然后根据middle求pre+suffix,由于pre==suffix,类似于回文串的情况,但是中间存在有其他字符串干扰的情况出现,这情况类似于KMP算法中的next数组的特点,next数组是前面i个字符所能匹配的最长的子串的下标,所以我们需要求next数组,当时我们不能直接求,需要将原字符串赋值一份到后面,然后将前面的翻转(如:ABCD-->DCBA#ABCD)中间的#起分隔符作用,求出了next数字,我们从#后面的A开始枚举,枚举最长pre+middle+suffix的情况。

代码:

#include<bits\stdc++.h>
#define N 200020
using namespace std;
int n,ex[N], nextVal[N], len[N], id[N], f[N];//len保存的是前i个字符的匹配最长的下标;//ex保存的是以每个点的半径 
char s[N], t[N];//s为原字符串,t为转化后的字符串
void manacher() {
	int po = 0, mx = 0;//len数组马拉车算法辅助数组
	for (int i = 1; i <= n; i++) {
		if (mx > i) {
			f[i] = min(f[2 * po - i], mx - i);
		}
		else {
			f[i] = 1;
		}
		while (t[i - f[i]] == t[i + f[i]]) {
			f[i]++;
		}
		if (i + f[i] > mx) {
			mx = f[i]+i;
			po = i;
		}
	}
	for (int m = 0, i = 2; i < n; i+=2) {
		ex[++m] = f[i] >> 1;
	}
}
int main() {
	int cnt = 0; 
	scanf("%s", s + 1);
	int m = strlen(s + 1);
	{//马拉车算法的准备 
		t[0] = '#';
		for (int i = 1; i <= m; i++) {
			t[++n] = '#';
			t[++n] = s[i];
		}
		t[++n] = '#';
	}
	manacher();//执行马拉车算法 
	{//求nextVal数组的准备
		s[m + 1] = '#';
		for (int i = 1; i <= m; i++) {
			s[m + 1 + i] = s[i];
		}
		reverse(s + 1, s + 1 + m);
	}
	{//求nextVal数组,变形的nextVal数组,因为-1被0代替 
		int j = -1;
		for (int i = 2; i <= 2*m+1;i++) {
			while ((j != 0) && s[j + 1] != s[i]) {
				j = nextVal[j];
			}
			j += (s[j + 1] == s[i]);
			nextVal[i] = j;
		}
	}
	
	int maxlen = 0, mIndex = 0, mLen = 0, pIndex = 0, pLen = 0, sIndex = 0, sLen = 0;//maxlen前缀+中缀+后缀的最大情况 
	for (int i = 1; i <= m; i++) {
		len[i] = len[i - 1];
		id[i] = id[i - 1];
		int tem = nextVal[m + 1 + i];
		if (tem > len[i]) {
			len[i] = tem;
			id[i] = i;
		}
		int j = ex[i];//以i为中心的半径 
		int r = min(len[i - j], m - i - j + 1);
		if (2 * r + 2 * j - 1 > maxlen) {
			maxlen = 2 * r + 2 * j - 1;
			mIndex = i - j + 1;
			mLen = 2 * j - 1;
			if (r != 0) {
				pLen = sLen = r;//前后缀的长度 
				pIndex = id[i - j] - r + 1;//前缀的索引
				sIndex = m - r + 1;//后缀的长度 
			}
			else {
				pLen = sLen = 0;
			}
		}
	}
	printf("%d\n", (pLen&&sLen) == 1 ? 3 : 1);//前缀与后缀的长度一样,故只有1和3的情况
	if (pLen)printf("%d %d\n", pIndex, pLen);
	printf("%d %d\n", mIndex, mLen);
	if (sLen)printf("%d %d\n", sIndex, sLen);
	return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值