Codeforces 906E Reverses

在这里插入图片描述

题意

给出两个串, s 和 t ,可以翻转 s 中的任意个不相交的区间,求变为 t 的最少操作次数

题解

变成 s 1 t 1 s 2 t 2 . . . s n t n s_1t_1s_2t_2...s_nt_n s1t1s2t2...sntn 的形式
转变为求最小的划分,使得每个部分都是回文串
Codeforces 932G 是求划分方案数的,这里把求和变为求最小值即可
注意
输出答案要把方案输出
所以,用 f [ i ] f[i] f[i] 不是表示 s [ 1... i ] s[1...i] s[1...i]划分成的最少次数,而是表示需要翻转的最少次数
更新时,要增加判断
s [ i ] = s [ i − 1 ] s[i]=s[i-1] s[i]=s[i1] ,说明原本的 s 和 t 对应位置是一样的,但是会误判成需要翻转一次,所以,需要 f [ i ] = f [ i − 2 ] , p a t h [ i ] = i − 2 f[i]=f[i-2],path[i]=i-2 f[i]=f[i2],path[i]=i2
输出的时候,若 i − 2 = = p a t h [ i ] i-2==path[i] i2==path[i] 说明实际上不需要翻转,跳过即可

代码
#include<bits/stdc++.h>
#define N 1000010
#define INF 0x3f3f3f3f
#define eps 1e-6
#define pi 3.141592653589793
#define mod 1000000007
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<"      :   "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y+3))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef  pair<int,int> pp;
int f[N],g[N],pa[N];
struct PAM {
    int next[N][26] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    int fail[N] ;//fail指针,失配后跳转到fail指针指向的节点
    int len[N] ;//len[i]表示节点i表示的回文串的长度
    int s[N] ;//存放添加的字符
    int last ;//指向上一个字符所在的节点,方便下一次add
    int n ;//字符数组指针
    int p ;//节点指针
    int d[N],up[N],id[N];
    inline int newnode ( int l ) {//新建节点
        for ( int i = 0 ; i < 26 ; ++ i ) next[p][i] = 0 ;
        len[p] = l ;
        return p ++ ;
    }
    inline void init () {//初始化
        p = 0 ;
        newnode (  0 ) ;
        newnode ( -1 ) ;
        last = 0 ;
        n = 0 ;
        s[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
        fail[0] = 1 ;
    }
    inline int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
        while ( s[n - len[x] - 1] != s[n] ) x = fail[x] ;
        return x ;
    }
    inline void add ( int c,int w ) {
        c -= 'a' ;
        s[++ n] = c ;
        int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
        if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode ( len[cur] + 2 ) ;//新建节点
            fail[now] = next[get_fail ( fail[cur] )][c] ; 
            next[cur][c] = now ;
            d[now]=len[now]-len[fail[now]];
            up[now]=(d[fail[now]]==d[now]?up[fail[now]]:now);
        }
        last = next[cur][c] ;
        id[w]=last;
    }
    void slove(){
        for(int i=1;i<=n;i++){
            for(int j=id[i];j;j=fail[up[j]]){
                g[j]=i-len[up[j]];
                if (d[j]==d[fail[j]]&&f[g[fail[j]]]<f[g[j]]) g[j]=g[fail[j]];
                if (!(i&1)&&f[g[j]]+1<f[i]){
                    f[i]=f[g[j]]+1;
                    pa[i]=g[j];
                }
            }
            if (!(i&1)&&s[i]==s[i-1]){
                f[i]=f[i-2];
                pa[i]=i-2;
            }
        }
    }
}A;
 
char s[N],t[N];
char ss[N];
int main(int argc, char const *argv[]){
    scanf("%s%s",s+1,t+1);
    int n=strlen(s+1);
    for(int i=1;i<=n*2;i+=2) ss[i]=s[i/2+1],ss[i+1]=t[i/2+1];
    A.init();   
    n<<=1;
    for(int i=1;i<=n;i++) A.add(ss[i],i);
    memset(f,INF,sizeof(int)*(n+3));
    f[0]=0;
    A.slove();  
    if (f[n]>n) puts("-1");else{
        printf("%d\n",f[n]);
        for(int i=n;i;i=pa[i]) 
            if (i-pa[i]>2)printf("%d %d\n",pa[i]/2+1,i/2);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值