现在的位置: 首页 > 综合 > 正文

bupt 204 Palindrome 求字符串任意区间的最长回文子串 二分答案+后缀数组

2013年04月29日 ⁄ 综合 ⁄ 共 8196字 ⁄ 字号 评论关闭

Description
 
Given a string S,you are asked to find the longest palindrome in the substring [l,r].

Input 

The first line is a number T which indicates the number of test cases
then follows a string s(length(s)<200000)
s can consists all ascii characters
Then an integer q,q querys(q<200000)
each query is two integer l,r.
find the longest palindrome in the substring [l,r].
Output
the answer for each query.

Sample Input

1
aaabbcc
5
1 7
1 4
2 3
4 5
2 5

Sample output
3
3
2
2
2

Hint
1 7 means aaabbcc the longest palindrome substring is aaa,whose length is 3.
4 5 means bb the longest palindrome substring is bb,whose length is 2.

// My Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
///后缀数组  倍增算法
const int maxn=400010;
int wa[maxn],wb[maxn],wv[maxn],wn[maxn],a[maxn],sa[maxn];
int cmp(int* r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
/**n为字符串长度,m为字符的取值范围,r为字符串。后面的j为每次排
序时子串的长度*/
void DA(int* r,int* sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb,*t;
    ///对R中长度为1的子串进行基数排序
    for(i=0;i<m;i++)wn[i]=0;
    for(i=0;i<n;i++)wn[x[i]=r[i]]++;
    for(i=1;i<m;i++)wn[i]+=wn[i-1];
    for(i=n-1;i>=0;i--)sa[--wn[x[i]]]=i;
    for(j=1,p=1;p<n;j*=2,m=p)
    {
        /**利用了上一次基数排序的结果,对待排序的子串的第二关键字进行
        了一次高效地基数排序*/
        for(p=0,i=n-j;i<n;i++)y[p++]=i;
        for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
        ///基数排序
        for(i=0;i<n;i++)wv[i]=x[y[i]];
        for(i=0;i<m;i++)wn[i]=0;
        for(i=0;i<n;i++)wn[wv[i]]++;
        for(i=1;i<m;i++)wn[i]+=wn[i-1];
        for(i=n-1;i>=0;i--)sa[--wn[wv[i]]]=y[i];
        ///当p=n的时候,说明所有串都已经排好序了
        ///在第一次排序以后,_rank数组中的最大值小于p,所以让m=p
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
    return;
}
///后缀数组  计算height数组
/**
height数组的值应该是从height[1]开始的,而且height[1]应该是等于0的。
原因是,+因为我们在字符串后面添加了一个0号字符,所以它必然是最小的
一个后缀。而字符串中的其他字符都应该是大于0的(前面有提到,使用倍
增算法前需要确保这点),所以排名第二的字符串和0号字符的公共前缀
(即height[1])应当为0.在调用calheight函数时,要注意height数组的范
围应该是[1..n]。所以调用时应该是calheight(r,sa,n)
而不是calheight(r,sa,n+1)。*/
int _rank[maxn],height[maxn];
void calheight(int* r,int* sa,int n)
{
    int i,j,k=0;
    for(i=1;i<=n;i++)_rank[sa[i]]=i;
    for(i=0;i<n;height[_rank[i++]]=k)
    for(k?k--:0,j=sa[_rank[i]-1];r[i+k]==r[j+k];k++);
    return;
}
//RMQ 求任意区间的最小值
int d[maxn];
int dpmin[maxn][20];
void creat_dpmin(int n)
{
    int i,j;
    for(i=1;i<=n;i++) dpmin[i][0]=d[i];
    for(j=1;j<=log(double(n+1))/log(2.0);j++)
    {
        for(i=1;i+(1<<j)-1<=n;i++)
        {
            dpmin[i][j]=min(dpmin[i][j-1],dpmin[i+(1<<(j-1))][j-1]);
        }
    }
}
//求任意区间的最小值
int get_min(int x,int y)
{
    int k=(int)(log(double(y-x+1))/log(2.0));
    return min(dpmin[x][k],dpmin[y-(1<<k)+1][k]);
}
//后缀数组 + RMQ
//求第i个后缀和第j个后缀的最长公共前缀
int get_min_lcp(int x,int y)
{
    x=_rank[x],y=_rank[y];
    if(x>y) swap(x,y);
    x++;//利用height数组
    int k=(int)(log(double(y-x+1))/log(2.0));
    return min(dpmin[x][k],dpmin[y-(1<<k)+1][k]);
}

int n1,n2;
char str1[maxn/2],str2[maxn/2];

int l[maxn/2],r[maxn/2];
int ans[maxn/2];

int dpmax[maxn/2][18];
void create_Dpmax1(int n){
     int i , j;
     for( i = 1 ; i <= n ; i++ )
          dpmax[i][0] = d[i];
     for( j = 1 ; j <= log((double)(n+1))/log(2.0) ; j++ ){
          for( i = 1 ; i+(1<<j)-1 <= n ; i++ ){
               dpmax[i][j] = max( dpmax[i][j-1] , dpmax[i+(1<<(j-1))][j-1] );
          }
     }
}

int getmax1( int a , int b ){
    int k = (int)(log((double)(b-a+1))/log(2.0));
    return max( dpmax[a][k] , dpmax[b-(1<<k)+1][k] );
}

void create_Dpmax2(int n){
     int i , j;
     for( i = 1 ; i <= n ; i++ )
          dpmax[i][0] = d[i+n1];
     for( j = 1 ; j <= log((double)(n+1))/log(2.0) ; j++ ){
          for( i = 1 ; i+(1<<j)-1 <= n ; i++ ){
               dpmax[i][j] = max( dpmax[i][j-1] , dpmax[i+(1<<(j-1))][j-1] );
          }
     }
}

int getmax2( int a , int b ){
    int k = (int)(log((double)(b-a+1))/log(2.0));
    return max( dpmax[a][k] , dpmax[b-(1<<k)+1][k] );
}

void read()
{
    scanf("%s",str1);memcpy(str2,str1,sizeof(str1));
    n1=strlen(str1),n2=strlen(str2);
    reverse(str2,str2+n2);
    for(int i=0;i<n1;i++) a[i]=(int)str1[i];
    a[n1]=280;
    for(int i=0;i<n2;i++) a[n1+i+1]=(int) str2[i];
    int n=n1+n2+1;
    a[n]=0;
    DA(a,sa,n+1,300);
    calheight(a,sa,n);
    //.........
    for(int i=1;i<=n;i++) d[i]=height[i];
    creat_dpmin(n);
    //int ans=0,pos;
    for(int i=0;i<n1;i++)//枚举回文串中心
    {
        int t=get_min_lcp(i,n1+n1-i)*2-1;//回文串是奇数情况 t回文串长度
        d[i+1]=t/2+1;//odd half
        //if(t>ans) ans=t,pos=i;
        if(i>0)//回文串是偶数数情况 t回文串长度
        {
            t=get_min_lcp(i,n1+n1-i+1)*2;
            d[i+n1]=t/2;//even half
            //if(t>ans) ans=t,pos=i;
        }
    }
    create_Dpmax1(n1);
    int q;scanf("%d",&q);int tq=q;
    int pl=0;
    while(q--)
    {
        scanf("%d%d",&l[pl],&r[pl]);
        if(l[pl]>r[pl]) swap(l[pl],r[pl]);
        int it=r[pl]-l[pl]+1;
        int low,high;
        //odd
        low=0;high=it+1;
        while(low<high)
        {
            int mid=(low+high)/2;
            //cout<<"low="<<low<<"..."<<"high="<<high<<"..."<<"mid="<<mid<<endl;
            int flag;
            int tl=l[pl]+mid-1,tr=r[pl]-mid+1;
            if(tl>tr) flag=0;
            else
            {
                int cnt=getmax1(tl,tr);
                //cout<<"tl="<<tl<<".."<<"tr="<<tr<<".."<<"cnt="<<cnt<<endl<<endl;
                if(cnt>=mid) flag=1;
                else flag=0;
            }
            if(flag) low=mid+1;
            else high=mid;
        }
        //cout<<"odd"<<"..."<<"low="<<low<<endl;
        ans[pl++]=(low-1)*2-1;
    }
    create_Dpmax2(n1-1);
    pl=0;
    while(tq--)
    {
        int it=r[pl]-l[pl]+1;
        int low,high;
        //even
        low=0,high=it+1;
        while(low<high)
        {
            int mid=(low+high)/2;
            int tl=l[pl]+mid-1,tr=r[pl]-mid;
            int flag;
            if(tl>tr) flag=0;
            else
            {
                int cnt=getmax2(tl,tr);
                if(cnt>=mid) flag=1;
                else flag=0;
            }
            if(flag) low=mid+1;
            else high=mid;
        }
        ans[pl]=max(ans[pl],2*(low-1));
        printf("%d\n",ans[pl++]);
    }
}
int main()
{
    int ci;scanf("%d",&ci);
    while(ci--)
    {
        read();
    }
    return 0;
}

//标程

#include<vector>
#include<list>
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<algorithm>
#include<functional>
#include<numeric>
#include<utility>
#include<iostream>
#include<sstream>
#include<iomanip>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<string>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<climits>
#include<complex>
#define sz(x) ((int)((x).size())))
#define rep(i,n) for (int i=0;i<n;i++)
#define mp make_pair
#define pb push_back
#define all(x) x.begin(),x.end()
#define clr(x) memset((x),0,sizeof(x))
using namespace std;
typedef vector<int> vi;
typedef vector<string> vs;
typedef pair<int,int> pii;
typedef vector<pii> vpi;
char s[500000],t[500000];
int len[500000];
void palindrome(char*cs)
{
    int n =strlen(cs);
    for (int i = 0, j = 0, k; i < n * 2; i += k, j = max(j - k, 0))
    {
        while (i - j >= 0 && i + j + 1 < n * 2
                && cs[(i - j) / 2] == cs[(i + j + 1) / 2]) j++;
        len[i] = j;
        for (k = 1; i - k >= 0 && j - k >= 0 && len[i - k] != j - k; k++)
        {
            len[i + k] = min(len[i - k], j - k);
        }
    }
}
const int maxn=250010;
int val[maxn],val1[maxn];
int Max[20][maxn],Max1[20][maxn];
int idx[maxn];
int n;
void init()
{
        idx[0]=-1;
        int i,j,k;
        for (i=1;i<=n;i++)
        {
                idx[i]=(i&(i-1))?idx[i-1]:idx[i-1]+1;
                Max[0][i]=val[i];
                Max1[0][i]=val1[i];
        }
        for (i=1;i<=idx[n];i++)
        {
                int lim=n+1-(1<<i);
                for (j=1;j<=lim;j++)
                {
                        Max[i][j]=max(Max[i-1][j],Max[i-1][j+(1<<i>>1)]);
                        Max1[i][j]=max(Max1[i-1][j],Max1[i-1][j+(1<<i>>1)]);
                }
        }
}
int getval(int x,int y)
{
        int t=idx[y-x+1];
        y-=(1<<t)-1;
        return max(Max[t][x],Max[t][y]);
}
int gaoval(int x,int y)
{
        int t=idx[y-x+1];
        y-=(1<<t)-1;
        return max(Max1[t][x],Max1[t][y]);
}
int main()
{
freopen("C:\\Users\\daizhy\\Documents\\output.txt","r",stdin);
freopen("C:\\Users\\daizhy\\Documents\\output1.txt","w",stdout);
int i,j,k,cas;
scanf("%d",&cas);
while (cas--)
{
scanf("%s",s);
int l=strlen(s);
palindrome(s);
        for (i=0;i<2*l-1;i+=2)
        {
                val[i/2+1]=len[i];  
        }
        for (i=1;i<2*l-1;i+=2)
        {
                val1[(i+1)/2]=len[i];
                //printf("%d %d\n",(i+1)/2,len[i]);
        }
        n=l;
        init();
int q;
scanf("%d",&q);
while (q--)
{
int x,y;
scanf("%d%d",&x,&y);
int low=0,high,mid;
int ans=1;
    int lt=(y-x+1);
    if (lt%2==1)high=lt/2;
    else high=(lt-1)/2;

while (low<=high)
    {
                        mid=(low+high)>>1;
                        int ll=2*mid+1;
                        int tmp=getval(x+mid,y-mid);
                        if (tmp>=ll)
                        {
                                low=mid+1;
                                ans=max(ans,ll);
                        }
                        else 
                        {
                                high=mid-1;
                        }
}
low=0;
if (lt%2==0)high=lt/2;
    else high=(lt-1)/2;
  while (low<=high)
    {
                        mid=(low+high)>>1;
                        int ll=2*mid;
                        int tmp=gaoval(x+mid-1,y-mid);
                        if (tmp>=ll)
                        {
                                low=mid+1;
                                ans=max(ans,ll);
                        }
                        else 
                        {
                                high=mid-1;
                        }
}
printf("%d\n",ans);
}
}
return 0;
}

抱歉!评论已关闭.