Girls' research(马拉车算法) hdu 3294

Problem Description One day, sailormoon girls are so delighted that
they intend to research about palindromic strings. Operation contains
two steps: First step: girls will write a long string (only contains
lower case) on the paper. For example, “abcde”, but ‘a’ inside is not
the real ‘a’, that means if we define the ‘b’ is the real ‘a’, then we
can infer that ‘c’ is the real ‘b’, ‘d’ is the real ‘c’ ……, ‘a’ is the
real ‘z’. According to this, string “abcde” changes to “bcdef”. Second
step: girls will find out the longest palindromic string in the given
string, the length of palindromic string must be equal or more than 2.

Input Input contains multiple cases. Each case contains two parts, a
character and a string, they are separated by one space, the character
representing the real ‘a’ is and the length of the string will not
exceed 200000.All input must be lowercase. If the length of string is
len, it is marked from 0 to len-1.

Output Please execute the operation following the two steps. If you
find one, output the start position and end position of palindromic
string in a line, next line output the real palindromic string, or
output “No solution!”. If there are several answers available, please
choose the string which first appears.

Sample Input

b babd
a abcd

Sample Output

0 2
aza
No solution!

思路如下

这题暴力准超时,但是还有有一种神奇的算法 Manachar 经过预处理后可以以O(n)来解决这个题
马拉车算法传送门
辅助理解转送门

Manachar代码注释

#include<iostream>
#include<string.h>
using namespace std;

const int Max = 1e5;
char ar[Max];           //原始字符串
char br[Max << 2];      //存储转化后的字串
int Len[Max << 2];      //Len[i] 计算为以br[i]字符为中心的回文子串的长度,还有要注意一个规律:Len[i] - 1 为以br[i]为中心的回文子串的组成字符个数


//转换字符串
int trans_ar(char ar[])
{
    int Len_ar = (int)strlen(ar);
    br[0] = '@';
    for(int i = 1; i <= 2 * Len_ar; i +=2)
    {
        br[i] = '#';
        br[i + 1] = ar[i / 2];
    }
    br[2 * Len_ar + 1] = '#';
    br[2 * Len_ar + 2] = '\0';
    return 2 * Len_ar + 1;
}
//Manachar 算法过程
int Manachar(char br[],int Len_br)
{
    int R = 0,mid = 0,ans = 0;      //R 为当前正计算的回文中心i之前的所有回文子串,所能延伸至的最右端 的下一个数
                                    //mid 为右端延伸至R处的回文子串的中心
    for(int i = 1; i <= Len_br; i ++)
    {
        if(i < R)
            Len[i] = min(R - i , Len[2 * mid - i]);     //2 * mid - i = j ( i 与 j 关于 mid对称,根据回味子串的对称性质,我们直接让 Len[i] = Len[j] 加速查找,但是Len[j] 要小于 R - mid)
                                                                        //举例证明 : c # a # c # a # b #
                                                                        //          1 2 3 4 5 6 7 8 9 10
        else
            Len[i] = 1;     //无法根据回文的性质进行加速了,只能老老实实的向两边延伸了

        while(br[i - Len[i]] == br[i + Len[i]])     //向两边延伸进行判断回文半径长度
            Len[i] ++;

        if(Len[i] + i > R)      //Len[i]  + i 当前以 i 为中心的回文子串所能延伸的最右端的距离
        {
            R = Len[i] + i;
            mid = i;
        }
        ans = max(ans , Len[i]);    //ans存储最大长度
    }
    return ans - 1;
}


int main()
{
    //freopen("test_3.txt","r",stdin);
    int n;
    cin>>n;
    for(int i = 0; i < n; i++)
        cin>>ar[i];

    //转化字符串ar为br,并获取转换问之后br的长度
    int Len_br = trans_ar(ar);
    //求最大回文子串的长度
    int ans = Manachar(br,Len_br);
    cout<<ans<<endl;

    return 0;
}

题解如下

//hdu 3294
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;

const int Len = 200005;
char ar[Len],br[Len << 2];
int Ridus[Len << 2];
char ch;

void change(char ar[])
{
    int Len_ar = (int)strlen(ar);
    int cha = ch - 'a';
    for(int i = 0; i < Len_ar; i++)
    {
        ar[i] -= cha;
        if (ar[i] < 'a')
            ar[i] += 26;
    }
}

int trans_ar(char ar[])
{
    int Ridus_ar = (int)strlen(ar);
    br[0] = '@';
    for(int i = 1; i <= 2 * Ridus_ar; i +=2)
    {
        br[i] = '#';
        br[i + 1] = ar[i / 2];
    }
    br[2 * Ridus_ar + 1] = '#';
    br[2 * Ridus_ar + 2] = '\0';
    return 2 * Ridus_ar + 1;
}

//Manachar 算法过程
int Manachar(char br[],int Ridus_br)
{
    int R = 0,mid = 0,ans = 0;      //R 为当前正计算的回文中心i之前的所有回文子串,所能延伸至的最右端 的下一个数
                                    //mid 为右端延伸至R处的回文子串的中心
    for(int i = 1; i <= Ridus_br; i ++)
    {
        if(i < R)
            Ridus[i] = min(R - i , Ridus[2 * mid - i]);     //2 * mid - i = j ( i 与 j 关于 mid对称,根据回味子串的对称性质,我们直接让 Ridus[i] = Ridus[j] 加速查找,但是Ridus[j] 要小于 R - mid)
        else
            Ridus[i] = 1;     //无法根据回文的性质进行加速了,只能老老实实的向两边延伸了

        while(br[i - Ridus[i]] == br[i + Ridus[i]])     //向两边延伸进行判断回文半径长度
            Ridus[i] ++;
        if(Ridus[i] + i > R)      //Ridus[i]  + i 当前以 i 为中心的回文子串所能延伸的最右端的距离
        {
            R = Ridus[i] + i;
            mid = i;
        }
        ans = max(ans , Ridus[i]);    //ans存储最大长度
    }
    return ans - 1;
}

int main()
{
//    freopen("test_3.txt","r",stdin);
    while(cin>>ch>>ar)
    {
        change(ar);
        int Len_br = trans_ar(ar);
        int ans = Manachar(br,Len_br);
        if(ans >= 2)
        {
            for(int i = 1;i <= Len_br; i ++)
            {
                if(Ridus[i] == ans + 1)
                {
                    int st = (i - ans) / 2;     //st * 2 + 2 = mid - r + 1
                    int ed = (i + ans - 2) / 2; //ed * 2 + 2 = mid + r - 1
                    cout<<st<<" "<<ed<<endl;
                    for(int j = i - ans; j <= i + ans; j ++)
                        if(br[j]>= 'a' && br[j] <= 'z')
                            cout<<br[j];
                    cout<<endl;
                    break;
                }
            }
        }
        else
            cout<<"No solution!\n";
    }
    return 0;
}
发布了73 篇原创文章 · 获赞 100 · 访问量 2693

猜你喜欢

转载自blog.csdn.net/qq_34261446/article/details/103947743