Mediocre String Problem (2018南京M,回文+LCP 3×3=9种做法 %%%千年好题 感谢"Grunt"大佬的细心讲解)
layout:     post
title:      Mediocre String Problem (2018南京M,回文+LCP 3×3=9种做法 %%%千年好题 感谢"Grunt"大佬的细心讲解)
author:     "luowentaoaa"
catalog:  true
mathjax: true
tags:
- 回文树
- 马拉车
- 扩展KMP
- 后缀数组
- 后缀自动机
- 字符串哈希
题意
给出一个串S,和一个串T.
要求 从S串中取一个子串,后面接上T串的一个前缀 组成一个结果串,(要求S串的部分比T串的部分长)
其中,S串贡献的部分 可以分成两部分,S1+S2;
前面的S1 是T部分的反转;
S2 就只能是回文串,因为S串的部分必须比T的多,所以S2长度必须大于等于1
然后我们可以分成两部分,首先先把S中的所有回文串求出,可以用(回文树/马拉车/字符串哈希)
对于每一个回文串,它的左边半径部分都可以作为S1的右端点,除了中心,而且边缘也可以吃到一个
比如 CABABA 其中 回文串中心是第二个A,S1的右端点可以是CAB 注意C也可以的哦
然后找出这个端点剩下的就是求S1和T的lcp的长度了,根据题意每一个长度都一个贡献一个符合要求的答案
针对这个问题 我们可以把S 反转 和T用EXKMP 求lcp (也可以用后缀数组/字符串哈希/exkmp/后缀自动机)
所以 这题的解法大概有(3×3=9 或者3×4=12)种。
做法1 :马拉车+EXKMP
///感谢Grunt 大佬的细心讲解
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pp pair<int,int>
const ll mod=998244353;
const int maxn=1e6+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
int lcm(int a,int b){return a*b/gcd(a,b);}
char s[maxn],t[maxn];
int lens,lent;
int mynext[maxn];
int extend[maxn];
ll sum[maxn];
void pre_EKMP(char x[],int m,int next[]){
    next[0]=m;
    int j=0;
    while(j+1<m&&x[j]==x[j+1])j++;
    next[1]=j;
    int k=1;
    for(int i=2;i<m;i++){
        int p=next[k]+k-1;
        int L=next[i-k];
        if(i+L<p+1)next[i]=L;
        else{
            j=max(0,p-i+1);
            while(i+j<m&&x[i+j]==x[j])j++;
            next[i]=j;
            k=i;
        }
    }
}
void EKMP(char x[],int m,char y[],int n,int next[],int extend[]){
    pre_EKMP(x,m,next);
    int j=0;
    while(j<n&&j<m&&x[j]==y[j])j++;
    extend[0]=j;
    int k=0;
    for(int i=1;i<n;i++){
        int p=extend[k]+k-1;
        int L=next[i-k];
        if(i+L<p+1)extend[i]=L;
        else{
            j=max(0,p-i+1);
            while(i+j<n&&j<m&&y[i+j]==x[j])j++;
            extend[i]=j;
            k=i;
        }
    }
}
char Ma[maxn*2];
int Mp[maxn*2];
void Manacher(char s[],int len){
    int l=0;
    Ma[l++]='$';
    Ma[l++]='#';
    for(int i=0;i<len;i++){
        Ma[l++]=s[i];
        Ma[l++]='#';
    }
    Ma[l]=0;
    int mx=0,id=0;
    for(int i=0;i<l;i++){
        Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
        while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++;
        if(i+Mp[i]>mx){
            mx=i+Mp[i];
            id=i;
        }
    }
}
ll getsum(int l,int r){
    if(l>r)return 0;
    else if(l<=0)return sum[r];
    else return sum[r]-sum[l-1];
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    cin>>s>>t;
    lens=strlen(s);lent=strlen(t);
    Manacher(s,lens);
    reverse(s,s+lens);
    EKMP(t,lent,s,lens,mynext,extend);
    reverse(extend,extend+lens);
    sum[0]=extend[0];
    for(int i=1;i<lens;i++){
        sum[i]=sum[i-1]+extend[i];
    }
    ll ans=0;                                     ///0 1 2 3 4
    for(int i=2;i<2*lens+3;i++){                  ///a b a b a
        int cnt=Mp[i]-1;      ///6-1=5;            $ # a # b # a # b # a #
        if(cnt==0||Mp[i]==0)continue;           ///0 1 2 3 4 5 6 7 8 9 10 11
        if(cnt&1){///奇数长度的回文 例如ababa  MP= 1 1 2 1 4 1 6 1 4 1  2  1  0
            int where=(i-2)/2;  ///找到a这个位置 =(6-1)/2=2;
            int r=where-1;      ///然后从a前面一个位置b作为右端点 =1
            int l=where-Mp[i]/2; ///然后找到左端点=2-6/2 =-1
            ans+=getsum(l,r);
        }
        else{                 /// 偶数的  例如aabbaa
            int where=(i-2-1)/2;   /// 找到b#b中间的‘#’ 左边的b
            int r=where-1;         ///右端点
            int l=where-cnt/2;    /// 左端点
            ans+=getsum(l,r);
        }
    }
    cout<<ans<<endl;
    return 0;
}
做法2. 回文自动机+EXKMP
题意:给S串与T串。S[i..j]+T[1..k]为回文串,且|S[i..j]|>|T[1..k]|,求(i,j,k)个数。
将S[i..j]分为两个部分,S[i..p]为T[1..k]的反转,S[p+1..j]为回文串。
由于|S[i..j]|>|T[1..k]|,所以S[p+1..j]必须不为空。
枚举回文串的起始点p+1,那么我们要求的是:
1.由于S[i..p]为T[1..k]的反转,我们只要求有多少个(i,k)。这个部分是exkmp的基础。
将S反转,跟T跑exkmp,求出ex[],再把ex[]反过来即可。
2.S[p+1..j]为回文串,我们只要求有多少个j,即求的是以p+1为起点的回文串个数cnt[]。
那么只要把S串倒着插入回文自动机,cnt[i]=插入第i个字符后,fail树的深度。
最后枚举回文串起点p+1,算出ex[p]*cnt[p+1],求和即为答案。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pp pair<int,int>
const ll mod=998244353;
const int maxn=1e6+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
int lcm(int a,int b){return a*b/gcd(a,b);}
char s[maxn],t[maxn];
int lens,lent;
int mynext[maxn];
int extend[maxn];
int num[maxn];
void pre_EKMP(char x[],int m,int next[]){
    next[0]=m;
    int j=0;
    while(j+1<m&&x[j]==x[j+1])j++;
    next[1]=j;
    int k=1;
    for(int i=2;i<m;i++){
        int p=next[k]+k-1;
        int L=next[i-k];
        if(i+L<p+1)next[i]=L;
        else{
            j=max(0,p-i+1);
            while(i+j<m&&x[i+j]==x[j])j++;
            next[i]=j;
            k=i;
        }
    }
}
void EKMP(char x[],int m,char y[],int n,int next[],int extend[]){
    pre_EKMP(x,m,next);
    int j=0;
    while(j<n&&j<m&&x[j]==y[j])j++;
    extend[0]=j;
    int k=0;
    for(int i=1;i<n;i++){
        int p=extend[k]+k-1;
        int L=next[i-k];
        if(i+L<p+1)extend[i]=L;
        else{
            j=max(0,p-i+1);
            while(i+j<n&&j<m&&y[i+j]==x[j])j++;
            extend[i]=j;
            k=i;
        }
    }
}
struct Palindromic_Tree{
    int next[maxn][26];
    int fail[maxn];
    int cnt[maxn];
    int num[maxn];
    int len[maxn];
    int S[maxn];
    int last;
    int n;
    int p;
    int newnode(int l){
        for(int i=0;i<26;i++)next[p][i]=0;
        cnt[p]=num[p]=0;len[p]=l;
        return p++;
    }
    void init(){
        p=0;
        newnode(0);
        newnode(-1);
        last=n=0;
        S[n]=-1;
        fail[0]=1;
    }
    int getfail(int x){
        while(S[n-len[x]-1]!=S[n])x=fail[x];
        return x;
    }
    int add(int c){
        c-='a';
        S[++n]=c;
        int cur=getfail(last);
        if(!next[cur][c]){
            int now=newnode(len[cur]+2);
            fail[now]=next[getfail(fail[cur])][c];
            next[cur][c]=now;
            num[now]=num[fail[now]]+1;
        }
        last=next[cur][c];
        cnt[last]++;
        return num[last];
    }
    void count(){
        for(int i=p-1;i>=0;i--)cnt[fail[i]]+=cnt[i];
    }
}pam;
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    cin>>s>>t;
    lens=strlen(s);lent=strlen(t);
    pam.init();
    for(int i=lens-1;i>=0;i--){
        num[i]=pam.add(s[i]);
    }
    reverse(s,s+lens);
    EKMP(t,lent,s,lens,mynext,extend);
    reverse(extend,extend+lens);
    ll ans=0;
    for(int i=0;i<lens-1;i++){
        ans+=1LL*extend[i]*num[i+1];
    }
    cout<<ans<<endl;
    return 0;
}
做法3.后缀数组+回文自动机(498ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pp pair<int,int>
const ll mod=998244353;
const int maxn=1e6+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
int lcm(int a,int b){return a*b/gcd(a,b);}
struct DA{
    #define F(x) ((x)/3+((x)%3==1?0:tb))
    #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
    int sa[maxn*20],rank[maxn*20],height[maxn*20],str[maxn*20];
    int wa[maxn*20],wb[maxn*20],wv[maxn*20],wss[maxn*20];
    int c0(int *r,int a,int b){
        return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
    }
    int c12(int k,int *r,int a,int b){
        if(k==2)
            return r[a]<r[b]||(r[a]==r[b]&&c12(1,r,a+1,b+1));
        else return r[a]<r[b]||(r[a]==r[b]&&wv[a+1]<wv[b+1]);
    }
    void sort(int *r,int *a,int *b,int n,int m){
        int i;
        for(i=0;i<n;i++)wv[i]=r[a[i]];
        for(i=0;i<m;i++)wss[i]=0;
        for(i=0;i<n;i++)wss[wv[i]]++;
        for(i=1;i<m;i++)wss[i]+=wss[i-1];
        for(i=n-1;i>=0;i--)
            b[--wss[wv[i]]]=a[i];
    }
    void dc3(int *r,int *sa,int n,int m){
        int i,j,*rn=r+n;
        int *san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
        r[n]=r[n+1]=0;
        for(i=0;i<n;i++)if(i%3!=0)wa[tbc++]=i;
        sort(r+2,wa,wb,tbc,m);
        sort(r+1,wb,wa,tbc,m);
        sort(r,wa,wb,tbc,m);
        for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
            rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
        if(p<tbc)dc3(rn,san,tbc,p);
        else for(i=0;i<tbc;i++)san[rn[i]]=i;
        for(i=0;i<tbc;i++)if(san[i]<tb)wb[ta++]=san[i]*3;
        if(n%3==1)wb[ta++]=n-1;
        sort(r,wb,wa,ta,m);
        for(i=0;i<tbc;i++)wv[wb[i]=G(san[i])]=i;
        for(i=0,j=0,p=0;i<ta&&j<tbc;p++)
            sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
        for(;i<ta;p++)sa[p]=wa[i++];
        for(;j<tbc;p++)sa[p]=wb[j++];
    }
    void da(int n,int m){
        for(int i=n;i<n*3;i++)str[i]=0;
        dc3(str,sa,n+1,m);
        int i,j,k=0;
        for(i=0;i<=n;i++)rank[sa[i]]=i;
        for(i=0;i<n;i++){
            if(k)k--;
            j=sa[rank[i]-1];
            while(str[i+k]==str[j+k])k++;
            height[rank[i]]=k;
        }
    }
    void print(int n){
        cout<<"sa[] ";
        for(int i=0;i<=n;i++)cout<<sa[i]<<" ";cout<<endl;
        cout<<"rank[] ";
        for(int i=0;i<=n;i++)cout<<rank[i]<<" ";cout<<endl;
        cout<<"height[] ";
        for(int i=0;i<=n;i++)cout<<height[i]<<" ";cout<<endl;
    }
}DA;
struct PalTree{
    int next[maxn][26],fail[maxn],cnt[maxn],num[maxn],len[maxn],S[maxn],last,n,p;
    int newnode(int l){
        for(int i=0;i<26;i++)next[p][i]=0;
        cnt[p]=num[p]=0;len[p]=l;return p++;
    }
    void init(){
        p=0;newnode(0);newnode(-1);last=0;n=0;S[n]=-1;fail[0]=1;
    }
    int get_fail(int x){
        while(S[n-len[x]-1]!=S[n])x=fail[x];return x;
    }
    int add(int c){
        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;num[now]=num[fail[now]]+1;
        }
        last=next[cur][c];cnt[last]++;return num[last];
    }
    void count(){for(int i=p-1;i>=0;i--)cnt[fail[i]]+=cnt[i];}
}PAM;
char s[maxn],t[maxn];
int num[maxn],cnt[maxn];
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    cin>>s>>t;
    int lens=strlen(s),lent=strlen(t);
    int len=0;PAM.init();
    for(int i=0;i<lens;i++)DA.str[len++]=s[lens-i-1]-'a'+1,cnt[lens-i-1]=PAM.add(s[lens-i-1]);
    DA.str[len++]=30;
    for(int i=0;i<lent;i++)DA.str[len++]=t[i]-'a'+1;
    DA.str[len]=0;
    DA.da(len,200);
	int p=DA.rank[lens+1];
	//DA.print(len);
	int now=len+1;
	for(int i=p-1;i>=0;i--){
		now=min(now,DA.height[i+1]);
		if(DA.sa[i]>=0&&DA.sa[i]<lens){
			num[lens-1-DA.sa[i]]=now;
		}
	}
	now=len+1;
	for(int i=p+1;i<=len;i++){
		now=min(now,DA.height[i]);
		if(DA.sa[i]>=0&&DA.sa[i]<lens){
			num[lens-1-DA.sa[i]]=now;
		}
	}
	ll ans=0;
    for(int i=0;i<lens-1;i++){
        ans+=1LL*num[i]*cnt[i+1];
    }
    cout<<ans<<endl;
    return 0;
}
感谢Grunt 大佬的细心讲解!!
///感谢Grunt 大佬的细心讲解
!(Mediocre String Problem/a.jpg)
毅种循环~
!(Mediocre String Problem/b.jpg)
Mediocre String Problem (2018南京M,回文+LCP 3×3=9种做法 %%%千年好题 感谢"Grunt"大佬的细心讲解)的更多相关文章
- ACM-ICPC2018南京赛区 Mediocre String Problem
		Mediocre String Problem 题解: 很容易想到将第一个串反过来,然后对于s串的每个位置可以求出t的前缀和它匹配了多少个(EXKMP 或者 二分+hash). 然后剩下的就是要处理以 ... 
- c语言求回文数的三种算法的描述
		c语言求回文数的三种算法的描述 题目描述 注意:(这些回文数都没有前导0) 1位的回文数有0,1,2,3,4,5,6,7,8,9 共10个: 2位的回文数有11,22,33,44,55,66,77,8 ... 
- Gym - 101981M The 2018 ICPC Asia Nanjing Regional Contest M.Mediocre String Problem Manacher+扩增KMP
		题面 题意:给你2个串(长度1e6),在第一个串里找“s1s2s3”,第二个串里找“s4”,拼接后,是一个回文串,求方案数 题解:知道s1和s4回文,s2和s3回文,所以我们枚举s1的右端点,s1的长 ... 
- [gym101981M][2018ICPC南京M题]Mediocre String Problem
		题目链接 题目大意是问在$S$串中找区间$[i,j]$,在$T$串中找位置$k$,使得$S[i,j]$和$T[1,k]$可以组成回文串,并且$j-i+1>k$,求这样的三元组$(i,j,k)$的 ... 
- LeetCode Problem 9:Palindrome Number回文数
		描述:Determine whether an integer is a palindrome. Do this without extra space. Some hints: Could nega ... 
- UVA 10739 String to Palindrome(动态规划 回文)
		String to Palindrome 题目大意:给出一个字符串s,现在可以进行3种操作(添加字母,删除字母,替换字母),将其变成回文串,求出最少的操作次数.比如abccda,可以用删除操作,删除b ... 
- 【LeetCode】 String中的最长回文
		java 普通版: 1.正序遍历数组,取得子字符串的首字母. 2.倒序遍历数组,取的子字符串的尾字母. (这样仅仅要在子循环中第一个出现回文,那么该回文肯定是本次循环的最长的回文) 3.新增数据结构, ... 
- PAT甲题题解-1040. Longest Symmetric String (25)-求最长回文子串
		博主欢迎转载,但请给出本文链接,我尊重你,你尊重我,谢谢~http://www.cnblogs.com/chenxiwenruo/p/6789177.html特别不喜欢那些随便转载别人的原创文章又不给 ... 
- HDU 5371——Hotaru's problem——————【manacher处理回文】
		Hotaru's problem Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) ... 
随机推荐
- django文件上传、图片验证码、抽屉数据库设计
			1.Django文件上传之Form方式 settings.py, ALLOWED_HOSTS = ['*'] INSTALLED_APPS = [ 'django.contrib.admin', 'd ... 
- a链接点击下载图片到本地(php)
			$url="http://pan.baidu.com/share/qrcode?w=150&h=150&url=http://gandao.my".U('Quest ... 
- virt-install command
			安装 virt-install --connect qemu:///system \ --virt-type=kvm \ --name windows2008 --ram --vcpus --arch ... 
- 解决使用vim-go插件时候保存go代码导致设置好的折叠消失的问题
			我之前在用vim编辑python代码的时候,折叠的功能都没啥问题 后来在编辑go代码的时候,我发现我一保存,折叠全都消失了,我很费解,就推断跟我使用的插件有关系,因为我保存的时候会触发gofmt插件格 ... 
- JSP/Servlet Web 学习笔记 DayFour
			Servlet概述 Servelt是使用Java Servlet应用程序接口及相关类和方法的Java程序. Servlet是用Java编写的Server端程序,它与协议和平台无关.Servlet运行于 ... 
- Access连接字符串
			Access2007没有密码连接: <connectionStrings> <add name="myconn" connectionString="P ... 
- 基于eclipse创建maven工程
			一.创建项目 1.Eclipse中用Maven创建项目 上图中Next 2.继续Next 3.选maven-archetype-webapp后,next 4.填写相应的信息,Packaged是默认创建 ... 
- 【转】oracle 删除重复记录
			转至:http://blog.163.com/aner_rui/blog/static/12131232820105901451809/ 2.保留一条(这个应该是大多数人所需要的 ^_^) Delet ... 
- [SDOI2015][bzoj4518] 征途 [斜率优化dp]
			题面 传送门 思路 把$vm^2$展开化一下式子,可以得到这样的等价公式: $vm^2=m\sum_{i=1}^m a_i^2-\sum_{i=1}^m a_i$ 那么我们要最小化的就是$\sum_{ ... 
- deeplearning4j——卷积神经网络对验证码进行识别
			一.前言 计算机视觉长久以来没有大的突破,卷积神经网络的出现,给这一领域带来了突破,本篇博客,将通过具体的实例来看看卷积神经网络在图像识别上的应用. 导读 1.问题描述 2.解决问题的思路 3.用DL ... 
