专栏文章

题解:P13790 「CZOI-R6」Border

P13790题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mio7a1my
此快照首次捕获于
2025/12/02 14:31
3 个月前
此快照最后确认于
2025/12/02 14:31
3 个月前
查看原文

题目大意

给定一个字符串 ss,至多修改其中一个字符,使其最长 border 尽量长。
border: 既是前缀又是后缀的子串。

分析

一开始智了,想的是二分,但是机房内阁同学写了一发 WA 了,才发现并没有单调性。

接下来是正解:

由于只能进行一次修改,所以前后缀修改后最多就改变两个位置(两个串有交时改中间)。
所以如果前后缀有 >2>2 个位置不同,就肯定不能成为 border
于是可以枚举 border 的长度(也就是答案),找到前后缀的第一个不同字符位置,修改后再判断是否相等(如果有两个位置不同,就判第一和第二个)。
只判第一个和第二个的原因就是上面的结论。
现在问题就是找到第一个不同位置和判相等了,这个直接二分(这下有单调性了) 再加个哈希就行了。

code

CPP
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

ll read()
{
    ll ret = 0, f = 1; char ch = getchar();
    while (!isdigit(ch)) f = (ch == '-' ? -f : f), ch = getchar();
    while (isdigit(ch)) ret = ret * 10 + ch - '0', ch = getchar();
    return ret * f;
}

int n;
const int N = 1e6 + 10;
char s[N];
ull h[N] ,pw[N] ,base = 1331;

ull get(int l ,int r) //获取 l ~ r 的子串 hash 值 
{
	if (l > r) return 0;
	return h[r] - h[l - 1] * pw[r - l + 1];
}

int main()
{
	scanf("%s",s + 1); n = strlen(s + 1);
	
	pw[0] = 1;
	for (int i = 1;i <= n;i++) pw[i] = pw[i - 1] * base;
	for (int i = 1;i <= n;i++) h[i] = h[i - 1] * base + s[i] - 'a'; //hash
	
	int ans = 0;
	for (int i = 1;i < n;i++)
	{
		int j = n - i + 1;
		int l = 1 ,r = i ,now = 0;
		while (l <= r) //二分找到最后一个相同的位置 
		{
			int mid = l + r >> 1;
			if (get(1 ,mid) == get(j ,j + mid - 1)) now = mid ,l = mid + 1;
			else r = mid - 1;
		}
		if (now == i) //直接匹配上 
		{
			ans = i;
			continue;
		}
		now++; //第一个不同的位置 
		bool f = false;
		
		//修改前缀的不同字符(第一个不同位置) 
		ull hs = get(1 ,i) - (s[now] - 'a') * pw[i - now]; //hs:前缀扣掉 now位置 的 hash 值 
		for (int c = 0;c < 26;c++) //枚举 now位置 替换成啥 
		{
			ull a = hs + c * pw[i - now] ,b = get(j ,n);
			
			//可能对后缀也有影响 
			if (j <= now) b += c * pw[n - now] - (s[now] - 'a') * pw[n - now];
			
			if (a == b) { f = true; break; } //找到可行修改 
		}
		if (f) { ans = i; continue; }
		
		//同理,修改后缀中的不同字符(第二个不同的位置)
		now = j + now - 1;
		hs = get(j ,n) - (s[now] - 'a') * pw[n - now];
		for (int c = 0;c < 26;c++)
		{
			ull b = hs + c * pw[n - now] ,a = get(1 ,i);
			if (i >= now) a += c * pw[i - now] - (s[now] - 'a') * pw[i - now];
			if (a == b) { f = true; break; }
		}
		if (f) ans = i;
	}
	
	printf("%d\n",ans);
	
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...