最小表示&Manacher

在一个字符串中找到一个循环同构使得该循环同构字典序最小

朴素算法:

暴力枚举每个串并比较O(n^2),实现时可用双指针,

force:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
template <typename T>
inline void read(T &a){
    a=0;bool b=0;char x=getchar();
    while(x<'0'||'9'<x){
        if(x=='-')b=1;
        x=getchar();
    }
    while('0'<=x&&x<='9'){
        a=(a<<1)+(a<<3)+x-'0';
        x=getchar();
    }
    if(b)a=-a;
}
char C[50];
int temp;
template<typename T>
inline void write(T a){
    if(a<0){
        putchar('-');
        a=-a;
    }
    do{
        C[++temp]=a%10+'0';
        a/=10;
    }while(a);
    while(temp)putchar(C[temp--]);
}
const int maxn=3e5+5;
int n,a[maxn*2];
int main(){
    read(n);
    for(int i=1;i<=n;i++){
        read(a[i]);
        a[n+i]=a[i];    
    }
    int i=1,j=2;//i表示可能是最小表示的位置 
    while(i<=n&&j<=n){
        if(a[j]<a[i])i=j++;
        else if(a[j]>a[i])j++;
        else{
            int k=1;
            while(a[i+k]==a[j+k]&&k<n)k++;
            //向后扫描找到长度内第一个 不相等的字符
            if(k<n){
                if(a[j+k]<a[i+k])i=j++;
                else j++;
            } 
        }
    }
    for(int p=0;p<n;p++){
        write(a[i+p]);putchar(' ');
    }
    return 0;
}

然后我们考虑为什么很慢,O(n)的比较是不能避免的了,所以我们从i的移动上做文章,

我们不难发现,i在移动时,如果a[i+k]!=a[j+k]

不妨设a[i+k]<a[j+k]:

那么假设a[j]~a[j+k]中有答案,那么一定是无论怎么比较都是最小的,所以a[j]~a[j+k]一定没有答案

证明结束,直接j=j+k+1

ac:

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=3e5+5;
template <typename T>
inline void read(T &a){
    a=0;bool b=0;char x=getchar();
    while(x<'0'||'9'<x){
        if(x=='-')b=1;
        x=getchar();
    }
    while('0'<=x&&x<='9'){
        a=(a<<1)+(a<<3)+x-'0';
        x=getchar();
    }
    if(b)a=-a;
}
char C[50];
int temp;
template<typename T>
inline void write(T a){
    if(a<0){
        putchar('-');
        a=-a;
    }
    do{
        C[++temp]=a%10+'0';
        a/=10;
    }while(a);
    while(temp)putchar(C[temp--]);
}
int n,a[maxn*2];
int main(){
    read(n);
    for(int i=1;i<=n;i++){
        read(a[i]);
        a[i+n]=a[i];
    }
    int i=1,j=2;//指针i,j表示可能的同构串的位置 
    while(i<=n&&j<=n){
        int k=0;
        while(a[i+k]==a[j+k]&&k<n)k++;
        if(k==n)break;//此时发现只有一个字符,直接结束
        if(a[i+k]<a[j+k]){
        //此时a[j]~a[j+k]都不可能是最小表示 
            j=j+k+1;//注意别写反了,此处的变换是排除!确实不可能!的答案
            if(i==j)j++;
        } 
        else {
            i=i+k+1;
            if(j==i)i++;
        }
    }
    if(i>n)i=j;
    for(int p=0;p<n;p++){
        write(a[i+p]);putchar(' ');
    }
    return 0;
}

二、Manacher:

求解字符串中最长回文子串

朴素算法:

枚举左右端点判断,O(n^3)

force 1:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cmath>
using namespace std;
template<typename T>
inline void read(T &a){
    a=0;bool b=0;char x=getchar();
    while(x<'0'||'9'<x){
        if(x=='-')b=1;
        x=getchar();
    }
    while('0'<=x&&x<='9'){
        a=(a<<1)+(a<<3)+x-'0';
        x=getchar();
    }
    if(b)a=-a;
}
char C[50];
int temp;
template<typename T>
inline void write(T a){
    if(a<0){
        putchar('-');
        a=-a;
    }
    do{
        C[++temp]=a%10+'0';
        a/=10;
    }while(a);
    while(temp)putchar(C[temp--]);
}
const int maxn=11000000;
char ch[maxn];
inline bool huiwen(int l,int r){
    while(ch[l]==ch[r]&&l<=r){l++;r--;}
    return l>r;
}

int main(){
    scanf("%s",ch+1);
    int len=strlen(ch+1);
    for(int i=len;i;i--){
        bool fd=0;
        for(int j=1;j<=len-i+1;j++){
            if(huiwen(j,j+i-1)){
            write(i);
            fd=1;
            break;
            }    
        }
        if(fd)break;
    }
    return 0;
}

枚举中点判断,O(n^2)

force 2:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cmath>
using namespace std;
template<typename T>
inline void read(T &a){
    a=0;bool b=0;char x=getchar();
    while(x<'0'||'9'<x){
        if(x=='-')b=1;
        x=getchar();
    }
    while('0'<=x&&x<='9'){
        a=(a<<1)+(a<<3)+x-'0';
        x=getchar();
    }
    if(b)a=-a;
}
char C[50];
int temp;
template<typename T>
inline void write(T a){
    if(a<0){
        putchar('-');
        a=-a;
    }
    do{
        C[++temp]=a%10+'0';
        a/=10;
    }while(a);
    while(temp)putchar(C[temp--]);
}
const int maxn=11000000;
char ch[maxn];
int n,ans;
int main(){
    scanf("%s",ch+1);
    n=strlen(ch+1);
    for(int i=1;i<=n;i++){
        //奇数串
        int len=1;
        while(ch[i+len]==ch[i-len]&&i-len&&i+len<=n)len++;
        ans=max(ans,(len-1)<<1|1);
        //偶数串 
        len=0;
        while(ch[i-len]==ch[i+len+1]&&i-len&&i+len<n)len++;
        ans=max(ans,len<<1);
    }
    write(ans);
    return 0;
}

 

posted @ 2019-06-20 19:37  Tj1  阅读(129)  评论(0编辑  收藏  举报