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 的字符串使得字典序最小:

  1. 删除掉末尾的一个字符
  2. 将字符串复制一遍并加到末尾(下文我们称之为自增)

题目分析:猜结论+暴力通过 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)

  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 所代表的前缀中,此时直接比较这两个位置即可
  2. 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>=ix,即 x > = i − x > = N e x t [ i − x ] x>=i-x>=Next[i-x] x>=ix>=Next[ix]
    所以我们此时已知了两个条件: 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[ix]
    下面设 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[ix] 的位置,直接比较即可

对于上面的情况二,画个图应该比较好理解

代码:

// 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)

[CodeForces7B]Memory Manager

Codeforces Round #456 (Div. 2)

Codeforces 1037E Trips图论dfs

C++ map erase问题