hdu1544 Palindromes

Palindromes

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1631    Accepted Submission(s): 788


Problem Description
A regular palindrome is a string of numbers or letters that is the same forward as backward. For example, the string "ABCDEDCBA" is a palindrome because it is the same when the string is read from left to right as when the string is read from right to left.

Now give you a string S, you should count how many palindromes in any consecutive substring of S.
 

Input
There are several test cases in the input. Each case contains a non-empty string which has no more than 5000 characters.

Proceed to the end of file.
 

Output
A single line with the number of palindrome substrings for each case. 
 

Sample Input
  
  
aba aa
 

Sample Output
  
  
4 3

题意是给定一个字符串,问说这个字符串中有多少个子串是回文串。
这道题可以dp做,dp[j][i]为从j到i是不是回文串,然后n2进行计数即可。
不过有o(n)的做法,Manacher法, 网上有很多博客讲的很好,我简单说一下自己的理解。
由于有奇偶数情况的出现,我们先在头尾以及每个字符串之间添加一个'#'或者其他没在字符串中出现过的字符,然后为了避免越界和方便处理我们再在再在最前面添加一个'$'。然后用一个辅助数组p[i]记录,以str[i]为中心的回文串向右延伸最大长度,这样实际上(p[i] - 1)就是原字符串中以该字符为中心的回文串长度(这个地方写一下就可以看出来),初始时为1;
比如cbaabc 变为 #c#b#a#a#b#c#
     1212127212121
假设我们当前处理到第i位,我们记录一下i左边的j里p[j]最大的j,用一个变量id记录一下这个j。再引入一个变量mx,mx = p[id] + id ;也就是以id为中心的回文串,向右最远能到达的位置。如果当前我们遇到的i,mx>i,那么说明str[i]是包含在以id为中心的回文串里的,所以p[i]的值可以借用str[i]的对称点str[j] ( j == 2 * id - i ) ,但是由于mx后面的字符串我们尚未到达过,所以p[i] + i 不能超过mx 因此可以有 p[i] = min( p[2 * id - i] ,mx - i ); 【最核心的思想就是这个p[i]的最小值选取,因为这里,实际上mx移动的过程只是 遍历了这个字符串一遍,因此只是o(n)】
最后我们∑(p[i] /2 )就是答案了;
 
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const int   MOD=1e9+7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
char str[300000];
int p[300000];
char tmp[200000];
int n ;
int  Manacher()
{
    int mx = 0;
    int id ;
    for(int i = 1 ; i < n ;++i)
    {
        if(mx > i)
            p[i] = min ( mx - i , p[ 2 * id - i]  ) ; // 2 * id - i 即为 与 i 关于id对称的字符 j 但是由于 mx 后面的字符尚未到过 , 所以有 mx - i
        else p[i] = 1;


        for( ; str[i - p[i]] == str[i + p[i]] ; )   p[i] ++  ;


        if(mx < i + p[i])
        {
            mx = p[i] + i;
            id = i ;
        }
    }
    int res = 1;
    for(int i =1 ; i< n ;++ i)
        res = max(res , p[i]);
    return res -1 ;
}
int main()
{
    while(~scanf("%s",tmp))
    {
    str[0] = '$' ;
    n = 1;
    int len = strlen(tmp);
    for(int i = 0 ; i < len; ++ i)
    {
        str[n++] = '#';
        str[n++] = tmp[i];
    }
    str[n++] = '#';
    str[n] = '\0';
    Manacher();
    LONG sum = 0 ;
    for(int i =1; i < n; i++)
        sum += (p[i]  )/ 2;
    cout<<sum<<endl;
}
}

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值