CodeForces - 1537E2 Erase and Extend (Hard Version)(扩展KMP-比较两个前缀无限循环后的字典序大小)
Posted Frozen_Guardian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CodeForces - 1537E2 Erase and Extend (Hard Version)(扩展KMP-比较两个前缀无限循环后的字典序大小)相关的知识,希望对你有一定的参考价值。
题目链接:点击查看
题目大意:给出一个长度为 n n n 的字符串 s s s,现在可以执行两种操作,构造一个长度为 m m m 的字符串使得字典序最小:
- 删除掉末尾的一个字符
- 将字符串复制一遍并加到末尾(下文我们称之为自增)
题目分析:猜结论+暴力通过 E 1 E1 E1 后应该不难发现,答案一定是字符串 s s s 的某个前缀,循环后取前 m m m 位作为答案
洛谷有个大神证明了贪心的正确性,这里不多赘述,写此博客是想记录一下
j
i
a
n
g
l
y
jiangly
jiangly 大神的结论:
a
i
n
f
<
b
i
n
f
<
=
>
a
+
b
<
b
+
a
a^{inf} < b^{inf} <=> a+b < b+a
ainf<binf<=>a+b<b+a
知道结论后还是不好实现,瓶颈在于快速比较两个字符串的大小,而两个字符串都是字符串 s s s 的前缀
这里需要利用扩展 K M P KMP KMP 优化,众所周知扩展 K M P KMP KMP 是利用了动态规划的思想, O ( n ) O(n) O(n) 预处理出 N e x t [ i ] Next[i] Next[i] 为 “后缀 s [ i : n ] s[i:n] s[i:n] 和字符串 s s s 的最长公共前缀的长度”
所以这里就可以分两种情况讨论了:假设 x x x 是我们找到的最优的前缀长度, i i i 是当前前缀的长度,现在的目的是为了确认 i i i 所代表的前缀能否更新 x x x 所代表的前缀,即是否满足 s [ 1 : i ] + s [ 1 : x ] < s [ 1 : x ] + s [ 1 : i ] s[1:i]+s[1:x]<s[1:x]+s[1:i] s[1:i]+s[1:x]<s[1:x]+s[1:i],其中加号是连接符号
那么我们的目标就是,对于字符串 a + b a+b a+b 和 b + a b+a b+a ,利用预处理好的 N e x t Next Next 数组,找到首个不相同的位置然后比较单个字符的大小,这样字符串比较的复杂度就是是 O ( 1 ) O(1) O(1) 了
- x + N e x t [ x ] < i x+Next[x]<i x+Next[x]<i: x + N e x t [ x ] x+Next[x] x+Next[x] 的意思是将 x x x 所代表的的前缀自增一次后,首个不同的位置,仍然位于 i i i 所代表的前缀中,此时直接比较这两个位置即可
-
x
+
N
e
x
t
[
x
]
>
=
i
x+Next[x]>=i
x+Next[x]>=i:放缩一下,因为
N
e
x
t
[
x
]
<
=
x
Next[x]<=x
Next[x]<=x,所以
x
+
x
>
=
x
+
N
e
x
t
[
x
]
>
=
i
x+x>=x+Next[x]>=i
x+x>=x+Next[x]>=i ,得到
x
+
x
>
=
i
x+x>=i
x+x>=i,两侧同时减去
x
x
x 得到
x
>
=
i
−
x
x>=i-x
x>=i−x,即
x
>
=
i
−
x
>
=
N
e
x
t
[
i
−
x
]
x>=i-x>=Next[i-x]
x>=i−x>=Next[i−x]。
所以我们此时已知了两个条件: x + N e x t [ x ] > = i x+Next[x]>=i x+Next[x]>=i 和 x > = N e x t [ i − x ] x>=Next[i-x] x>=Next[i−x]
下面设 x x x 代表的字符串为 a a a, i i i 代表的字符串为 b b b,即满足 ∣ a ∣ < ∣ b ∣ |a|<|b| ∣a∣<∣b∣
第一个条件,意味着字符串 a + b a+b a+b 和 b + a b+a b+a 的前 b b b 项一定是相等的。
第二个条件,意味着 a + b a+b a+b 和 b + a b+a b+a 的首个不相等的位置出现在 N e x t [ i − x ] Next[i-x] Next[i−x] 的位置,直接比较即可
对于上面的情况二,画个图应该比较好理解
代码:
// Problem: E2. Erase and Extend (Hard Version)
// Contest: Codeforces - Codeforces Round #726 (Div. 2)
// URL: https://codeforces.com/contest/1537/problem/E2
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
#include<unordered_map>
#define lowbit(x) x&-x
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)
{
T f=1;x=0;
char ch=getchar();
while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
x*=f;
}
template<typename T>
inline void write(T x)
{
if(x<0){x=~(x-1);putchar('-');}
if(x>9)write(x/10);
putchar(x%10+'0');
}
const int inf=0x3f3f3f3f;
const int N=1e6+100;
int Next[N];
char s[N];
//预处理计算Next数组
void getNext(const char str[])
{
int i=0,j,po,len=strlen(str);
Next[0]=len; //初始化Next[0]
while(str[i]==str[i+1] && i+1<len) i++; Next[1]=i; //计算Next[1]
po=1; //初始化po的位置
for(i=2;i<len;i++)
{
if(Next[i-po]+i < Next[po]+po) //第一种情况,可以直接得到Next[i]的值
Next[i]=Next[i-po];
else //第二种情况,要继续匹配才能得到Next[i]的值
{
j = Next[po]+po-i;
if(j<0) j=0; //如果i>po+Next[po],则要从头开始匹配
while(i+j<len && str[j]==str[j+i]) j++; Next[i]=j;
po=i; //更新po的位置
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n,m;
read(n),read(m);
scanf("%s",s);
getNext(s);
int x=1;
for(int i=2;i<=n;i++) {//i代表的是前缀s[0:i-1]
if(x+Next[x]<i) {
if(s[x+Next[x]]<s[Next[x]]) {
x=i;
}
} else if(Next[i-x]<=x) {//now Next[i-x]<=x
if(s[Next[i-x]]<s[i-x+Next[i-x]]) {以上是关于CodeForces - 1537E2 Erase and Extend (Hard Version)(扩展KMP-比较两个前缀无限循环后的字典序大小)的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces1303E. Erase Subsequences
Codeforces1537 E2. Erase and Extend (Hard Version)(lcp)