CF 119D String Transformation 题解

emm不知道为啥会评黑……其实不难(虽然看了题解才想起来有哈希这玩意)。

题目给了一种翻转操作,乍一看无从下手,但是如果你画一画示意图,会发现很多东西。

如图,我们用箭头表示方向,颜色表示三个不同的部分。这样看的话,我们发现,标号为 \(2\) 的部分可以用 KMP 处理,但是 \(1\)\(3\) 貌似没法处理。

pCVE6JI.png

这时候,让我们把原串 \(s\) 反过来看。我们让新串叫做 \(fs\),怎么样,是不是有发现! \(t\)\(fs\) 的后缀部分都是 \(3\) 号串,而 \(1\) 号串部分我们可以用 KMP 来处理,剩下的我们只需要判断 \(2\) 号串是否相同即可。这个可以用哈希表。因为要求 \(i\) 最大,\(j\) 最小,所以我们用 KMP 处理出 \(1\) 号串的极左端点,从小到大枚举 \(1\) 号串右端点的位置,这样就可以保证答案限制了。

pCVEyFA.png

但是,还没完。这里有个正确性的问题,那就是,我们处理出极长字串是否正确。为什么这样说?因为我们发现,题目要求 \(j\) 最小,而我们处理出的极长字串其实是让 \(j\) 变大了。但其实根本没必要担心,因为如果存在这样的一个串,那么答案会提前更新完毕。至于证明,可以自己画一画,标出相同的部分(才不是因为我懒呢)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+100;
const int pa = 23, pb = 233, ma = 998244353, mb = 1000000007;
int n;

ll hasha[2][N], hashb[2][N], spa[N], spb[N], hashc[N];
char s[N], fs[N];
char t[N];
int nxt2[N], nxt1[N];
int ansl = -1, ansr = -1;
void init(){
    spa[0] = spb[0] = 1; 
    for(int i = 1; i<=n; i++){
        hasha[0][i] = (hasha[0][i-1]*pa%ma+s[i])%ma;
        hasha[1][i] = (hasha[1][i-1]*pa%ma+t[i])%ma;
        hashc[i] = (hashc[i-1]*pa%ma+fs[i])%ma;
        spa[i] = spa[i-1]*pa%ma;
    }
    for(int i = 1; i<=n; i++){
        hashb[0][i] = (hashb[0][i-1]*pb%mb+s[i])%mb;
        hashb[1][i] = (hashb[1][i-1]*pb%mb+t[i])%mb;
        spb[i] = spb[i-1]*pb%mb;
    }
}
void check(int l, int r){
    if(ansl<l){
        ansl = l, ansr = r;
    }else if(ansl == l&&ansr>r){
        ansr = r;
    }
}
inline ll geta(int l, int r, int op){
    return ((hasha[op][r]-hasha[op][l-1]*spa[r-l+1]%ma)%ma+ma)%ma;
}
inline ll getb(int l, int r, int op){
    return ((hashb[op][r]-hashb[op][l-1]*spb[r-l+1]%mb)%mb+mb)%mb;
}
inline ll getc(int l, int r){
	return ((hashc[r]-hashc[l-1]*spa[r-l+1]%ma)%ma+ma)%ma;
}
void KMP(){
    int j = 0;
    for(int i = 2; i<=n; i++){
        while(j&&fs[j+1]!=fs[i]){
            j = nxt1[j];
        }
        if(fs[j+1] == fs[i])++j;
        nxt1[i] = j;
    }
    j = 0;
    for(int i = 1; i<=n; i++){
        while(j&&fs[j+1]!=t[i]){
            j = nxt1[j];
        }
        if(fs[j+1] == t[i]) j++;
        nxt2[i] = j;//求出1号串左端的极大位置。
    }
    for(int i = 1; i<n; i++){
        int pos = nxt2[i];
        if(!pos) continue;
        if(geta(i+1, n, 1) == getc(i+1, n)&&geta(1,i-pos, 1)==geta(n-i+1, n-pos, 0)&&getb(1,i-pos, 1)==getb(n-i+1, n-pos, 0)){
            check(n-i-1, n-pos);
        }
    }

}
int main(){
    cin.getline(s+1, 1000006);
    cin.getline(t+1, 1000006);
    n = strlen(s+1);
    int mtp = strlen(t+1);
    if(n ^ mtp){
    	puts("-1 -1");
    	return 0;
	}
    for(int i = 1; i<=n; i++){
        fs[i] = s[n-i+1];
    }
    init();
    KMP();
    printf("%d %d\n", ansl, ansr);
    return 0;
}
posted @ 2023-06-10 19:52  霜木_Atomic  阅读(25)  评论(0编辑  收藏  举报