GYM-102361J MUV LUV EXTRA kmp求最小循环节

GYM-102361J MUV LUV EXTRA kmp求最小循环节

题意

给定正整数(a,b)求最大的(a imes p - b imes l),其中(p)表示后缀的循环节的总长度,(l)表示这个最小循环节的长度

[1 leq a,bleq 1e9\ 1 leq |s| leq 1e7 ]

分析

求这个最大值,不妨枚举(p),逆序字符串,这样问题就转化成了求最小循环节。

对于求最小循环节(可以在前面补),可以利用KMP求得next数组,长度为(n)的字符串的最小循环节长度就是(n - next[n])

这好像并不显然,不严谨证明如下:

代码

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;

ll rd(){
	ll x = 0;
	int f = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-') f = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}

const int maxn = 1e7 + 5;
int Next[maxn];
char s1[maxn],s2[maxn];

int len1,len2;

void getNext(){
	for(int i = 2,j = 0;i <= len2;i++){
		while(s2[i] != s2[j + 1] && j > 0) j = Next[j];
		if(s2[i] == s2[j + 1]) Next[i] = ++j; 
	}
}

int main(){
	ll a,b;
	while(~scanf("%lld%lld",&a,&b)){
		scanf("%s",s1 + 1);
		len1 = 	strlen(s1 + 1);
		int pos = -1;
		for(int i = 1;i <= len1;i++){
			if(s1[i] == '.') {
				pos = i + 1;
				break; 
			} 
		} 
		strcpy(s2 + 1,s1 + pos);
		len2 = strlen(s2 + 1);
		reverse(s2 + 1,s2 + len2 + 1);
		getNext();
		ll ans = a - b;
		for(int i = 1;i <= len2;i++){
			ans = max(ans,a * i - b * (i - Next[i]));
		}
		cout << ans << '
';	
	}
}
原文地址:https://www.cnblogs.com/hznumqf/p/14552168.html