UVA - 11475 Extend to Palindrome (后缀数组)

Your task is, given an integer N, to make a palidrome (word that reads the same when you reverse it) of length at least N. Any palindrome will do. Easy, isn’t it? That’s what you thought before you passed it on to your inexperienced team-mate. When the contest is almost over, you find out that that problem still isn’t solved. The problem with the code is that the strings generated are often not palindromic. There’s not enough time to start again from scratch or to debug his messy code. Seeing that the situation is desperate, you decide to simply write some additional code that takes the output and adds just enough extra characters to it to make it a palindrome and hope for the best. Your solution should take as its input a string and produce the smallest palindrome that can be formed by adding zero or more characters at its end. Input Input will consist of several lines ending in EOF. Each line will contain a non-empty string made up of upper case and lower case English letters (‘A’-‘Z’ and ‘a’-‘z’). The length of the string will be less than or equal to 100,000. Output For each line of input, output will consist of exactly one line. It should contain the palindrome formed by adding the fewest number of extra letters to the end of the corresponding input string. Sample Input aaaa abba amanaplanacanal xyz Sample Output aaaa abba amanaplanacanalpanama

 

题意:

在字符串末尾附加最少的字母,使其成为回文串。

思路:

求得字符串结尾处的回文串长度,保持结尾处回文串不变,其他的翻转一下即可。

注意使用后缀数组时,要对后缀的起始位置进行约束,否则会出错,如:aaaaaaabaaaa

UVA - 11475 Extend to Palindrome (后缀数组)
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>

#define fuck(x) cerr<<#x<<" = "<<x<<endl;
#define debug(a, x) cerr<<#a<<"["<<x<<"] = "<<a[x]<<endl;
#define ls (t<<1)
#define rs ((t<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 200086;
const int maxm = 100086;
const int inf = 0x3f3f3f3f;
const ll Inf = 999999999999999999;
const int mod = 1000000007;
const double eps = 1e-6;
const double pi = acos(-1);

char s[maxn];
int len, Rank[maxn], sa[maxn], tlen, tmp[maxn];

bool compare_sa(int i, int j) {
    if (Rank[i] != Rank[j]) { return Rank[i] < Rank[j]; }
    //如果以i开始,长度为k的字符串的长度,已经超出了字符串尾,那么就赋值为-1
    //这是因为,在前面所有数据相同的情况下,字符串短的字典序小.
    int ri = i + tlen <= len ? Rank[i + tlen] : -inf;
    int rj = j + tlen <= len ? Rank[j + tlen] : -inf;
    return ri < rj;
}

void construct_sa() {
    //初始的RANK为字符的ASCII码
    for (int i = 0; i <= len; i++) {
        sa[i] = i;
        Rank[i] = i < len ? s[i] : -inf;
    }
    for (tlen = 1; tlen <= len; tlen *= 2) {
        sort(sa, sa + len + 1, compare_sa);
        tmp[sa[0]] = 0;
        //全新版本的RANK,tmp用来计算新的rank
        //将字典序最小的后缀rank计为0
        //sa之中表示的后缀都是有序的,所以将下一个后缀与前一个后缀比较,如果大于前一个后缀,rank就比前一个加一.
        //否则就和前一个相等.
        for (int i = 1; i <= len; i++) {
            tmp[sa[i]] = tmp[sa[i - 1]] + (compare_sa(sa[i - 1], sa[i]) ? 1 : 0);
        }
        for (int i = 0; i <= len; i++) {
            Rank[i] = tmp[i];

        }
    }
}

int height[maxn];

void construct_lcp() {
//    for(int i=0;i<=n;i++){Rank[sa[i]]=i;}
    int h = 0;
    height[0] = 0;
    for (int i = 0; i < len; i++) {//i为后缀数组起始位置
        int j = sa[Rank[i] - 1];//获取当前后缀的前一个后缀(排序后)
        if (h > 0)h--;
        for (; j + h < len && i + h < len; h++) {
            if (s[j + h] != s[i + h])break;
        }
        height[Rank[i]] = h;
    }
}

int st[maxn][20];

void rmq_init() {
    for (int i = 1; i <= len; i++) {
        st[i][0] = height[i];
    }
    int l = 2;
    for (int i = 1; l <= len; i++) {
        for (int j = 1; j + l / 2 <= len; j++) {
            st[j][i] = min(st[j][i - 1], st[j + l / 2][i - 1]);
        }
        l <<= 1;
    }
}

int ask_min(int i, int j) {
    int k = int(log(j - i + 1.0) / log(2.0));
    return min(st[i][k], st[j - (1 << k) + 1][k]);
}

int lcp(int a, int b)//此处参数是,原字符串下标
{
    a = Rank[a], b = Rank[b];
    if (a > b)
        swap(a, b);
    return ask_min(a + 1, b);
}

int solve(){
    int ans=1,pos=0;
    for(int i=1;i<=len;i++){
        if(sa[i]==len/2+1){
            pos=i;
            break;
        }
    }
    int mn=inf;
    for(int i=pos+1;i<=len;i++){
        mn=min(height[i],mn);
        if(sa[i]==len/2-mn){
            ans=max(mn,ans);
        }
    }
    mn=inf;
    for(int i=pos;i>=1;i--){
        mn=min(height[i],mn);
        if(sa[i-1]==len/2-mn){
            ans=max(mn,ans);
        }
    }

    return ans;

}

int main() {
//    ios::sync_with_stdio(false);
//    freopen("in.txt", "r", stdin);

    while (scanf("%s",s)!=EOF){
        len=strlen(s);
        s[len]='$';
        for(int i=0;i<len;i++){
            s[len*2-i]=s[i];
        }

        len=len*2+1;
        height[len+1]=0;
        s[len]=0;
        construct_sa();
        construct_lcp();
        int l=0,r=len;
        int ans=solve();
        len/=2;
        int tmps=len-ans;
        for(int i=0;i<tmps;i++){
            s[len+i]=s[len-ans-i-1];
        }
        s[len+tmps]=0;
        printf("%s\n",s);
    }

    return 0;
}
View Code

 

上一篇:LeetCode - 125. Valid Palindrome


下一篇:教育行业优质解决方案分享【科研云解决方案】