zoukankan      html  css  js  c++  java
  • 非确定性有穷状态决策自动机练习题Vol.1 A.扭动的回文串

    非确定性有穷状态决策自动机练习题Vol.1 A.扭动的回文串

    题目描述

    (JYY)有两个长度均为(N)的字符串(A)(B)

    一个“扭动字符串(S(i,j,k))(A)中的第(i)个字符到第(j)个字符组成的子串

    与B中的第(j)个字符到第(k)个字符组成的子串拼接而成。

    比如,若(A)=’XYZ’,(B)=’UVW’,则扭动字符串(S(1,2,3))=’XYVW’。

    (JYY)定义一个“扭动的回文串”为如下情况中的一个:

    1.(A)中的一个回文串;

    2.(B)中的一个回文串;

    3.或者某一个回文的扭动字符串(S(i,j,k))

    现在(JYY)希望找出最长的扭动回文串。

    输入格式

    第一行包含一个正整数(N)

    第二行包含一个长度为(N)的由大写字母组成的字符串A。

    第三行包含一个长度为(N)的由大写字母组成的字符串B。

    输出格式

    输出的第一行一个整数,表示最长的扭动回文串。

    样例

    样例输入

    5
    ABCDE
    BAECB

    样例输出

    5

    样例解释

    最佳方案中的扭动回文串如下所示(不在回文串中的字符用.表示):

    .BC..

    ..ECB

    数据范围与提示

    对于(10\%)的数据:(N≤100)

    对于(30\%)的数据:(N≤1000)

    对于(50\%)的数据:(N≤10000);

    对于(100\%)的数据:(1≤N≤10^5)

    分析

    对于只在 (A) 中的回文串和只在 (B) 中的回文串,我们直接拿马拉车 (O(n)) 解决即可

    比较难处理的是扭动回文串的情况

    显然,对于每一个回文串,都有一个回文中心(在马拉车算法前,我们已经把所有的偶回文串填充成了奇回文串)

    我们可以把 (A) 串和 (B) 串中的每一个字符都当作回文中心计算贡献

    在马拉车算法后,我们已经求出了以 (i) 作为回文中心的最长回文半径 (f[i])

    我们只需要在原来的基础上继续向外扩展即可

    对于 (A) 串中的字符,我们在原串上向左扩展,在 (B) 串上向右扩展

    对于 (B) 串中的字符,我们在原串上向右扩展,在 (A) 串上向左扩展

    直接暴力去扫肯定会 (T) ,我们可以用二分+ (Hash) 将扩展的复杂度降为 (log(n))

    对于 (A) 我们正着记录一遍哈希值,对于 (B) 我们倒着记录一遍哈希值即可

    代码

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int maxn=1e6+5;
    typedef unsigned long long ll;
    const ll bas=233;
    char a[maxn],b[maxn],ksa[maxn],ksb[maxn];
    int fa[maxn],fb[maxn],n;
    ll hasha[maxn],hashb[maxn],mi[maxn];
    inline int read(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') f=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*f;
    }
    int js=0;
    ll get_hasha(int l,int r){
    	return hasha[r]-hasha[l-1]*mi[r-l+1];
    }
    ll get_hashb(int l,int r){
    	return hashb[l]-hashb[r+1]*mi[r-l+1];
    }
    //取哈希值
    int solvea(int id){
    	int l=id-fa[id]+1,r=id+fa[id]-1;
    	int nl=0,nr=min(l-1,n-r),nmids;
    	while(nl<=nr){
    		nmids=(nl+nr)>>1;
    		if(get_hasha(l-nmids,l)==get_hashb(r-2,r+nmids-2)) nl=nmids+1;
    		else nr=nmids-1;
    	}
    	return fa[id]-1+nr;
    }
    int solveb(int id){
    	int l=id-fb[id]+1,r=id+fb[id]-1;
    	int nl=0,nr=min(l-1,n-r),nmids;
    	while(nl<=nr){
    		nmids=(nl+nr)>>1;
    		if(get_hasha(l-nmids+2,l+2)==get_hashb(r,r+nmids)) nl=nmids+1;
    		else nr=nmids-1;
    	}
    	return fb[id]-1+nr;
    }
    //对于两个串分别二分
    int main(){
    	n=read();
    	scanf("%s%s",ksa+1,ksb+1);
    	n=n*2+1;
    	a[0]='$',b[0]='$';
    	for(int i=1;i<=n;i++){
    		if(i&1) a[i]='#';
    		else a[i]=ksa[i/2];
    	}
    	for(int i=1;i<=n;i++){
    		if(i&1) b[i]='#';
    		else b[i]=ksb[i/2];
    	}
    	mi[0]=1;
    	for(int i=1;i<=n;i++){
    		mi[i]=mi[i-1]*bas;
    		hasha[i]=hasha[i-1]*bas+a[i];
    	}
    	for(int i=n;i>=1;i--){
    		hashb[i]=hashb[i+1]*bas+b[i];
    	}
    	//预处理哈希值
    	int mids=0,r=0,ans=0;
    	for(int i=1;i<=n;i++){
    		if(i<=r) fa[i]=min(fa[mids*2-i],r-i+1);
    		while(a[i+fa[i]]==a[i-fa[i]]) fa[i]++;
    		if(i+fa[i]-1>r){
    			r=i+fa[i]-1,mids=i;
    			ans=max(ans,fa[i]-1);
    		}
    	}
    	mids=0,r=0;
    	for(int i=1;i<=n;i++){
    		if(i<=r) fb[i]=min(fb[mids*2-i],r-i+1);
    		while(b[i+fb[i]]==b[i-fb[i]]) fb[i]++;
    		if(i+fb[i]-1>r){
    			r=i+fb[i]-1,mids=i;
    			ans=max(ans,fb[i]-1);
    		}
    	}
    	//马拉车
    	for(int i=1;i<=n;i++){
    		ans=max(ans,solvea(i));
    		ans=max(ans,solveb(i));
    	}
    	printf("%d
    ",ans);
    	return 0;	
    }
    
  • 相关阅读:
    web报表工具FineReport常用函数的用法总结(日期和时间函数)
    web报表工具FineReport常用函数的用法总结(文本函数)
    实例:供应商管理报表需求调研报告
    电信业务支撑报表系统解决方案
    小程序开发快速入门教程(附源码)
    不用代码,10分钟打造属于自己的第一款小程序
    移动端iPhone系列适配问题的一些坑
    【前端统计图】echarts多条折线图和横柱状图实现
    不用代码,10分钟打造属于自己的第一款小程序
    markdown模式的一些语法
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/13519451.html
Copyright © 2011-2022 走看看