UESTC CDOJ 1551 Hesty Str1ng 后缀数组+乱搞

题目链接:Hesty Sr1ng

Hesty Str1ng
Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)

Submit Status
A chrysanthemum was painted on the second page, and we tried to use the magic power learned just now.
The beautiful flower was compacted to a colorful string S representing by characters for some simplifying reasons.
As known, if we try to choose a substring A of S and concatenate it with an arbitrary non-empty string B whose length is less than A, we can get a new string T.
The poetry told us the key to the eternity living secret is the number of the distinct palindrome strings made by the method above.
Two strings are considered different if and only if there are different characters in the same position of two strings.


Input
Only one line contains the string S.


S is composed by lowercase English characters, 1≤|S|≤100000.


Output
The key to the eternity living secret.


Sample input and output
Sample Input 

abc

Sample Output
3
Hint
The palindrome strings can be made are "aba", "bcb", "abcba".


题意:选出一个子串,然后在后面填充少于子串的字符成一个回文串,问能组成多少组不同的回文串。
题目分析:刚开始看这题很直白以为是求不同的子串个数,后缀数组模板拍上去果然WA,之后才发现不是这样的。一下是具体的思路。
1、首先对于一般情况找到一个子串,然后要求填充的字符要小于子串,所以直接以最后一个字符为轴填充回文串即可,此时不需要考虑本身已经构成回文串或者一部分是回文串,假如有m个不同的子串,那就有m种不同的回文串,对于求不同子串的个数,可以套用后缀数组的模板,然后统计n-sa[i]-height[i]即可。

2、可能有的子串末尾2位是相同的,这时可以构造出偶数长度的回文串,同样如果本身已构成一部分回文串的部分不需要考虑,因为会和之前统计的重合,这里我们就要求出所有不同的末尾2位相同的子串,注意这个子串前面一定还要有字符,因为填充长度大于0.我的方法是:从第二位开始,统计和后一个字符相同 的位置,用mark[i]来存从第i位到最后一位满足上述条件的字符个数,之后会用到。然后使用后缀数组中统计到的sa数组和height数组,sa数组代表排名第i的后缀的起始位置,height数组代表排名第i的后缀和第i+1的后缀最长公共前缀,对于公共的前缀不予考虑,所以只需考虑sa[i]+height[i]后面的字符,以此来避免重复子串,从sa[i]到sa[i]+height[i]后面所有的前缀如果有末尾两位相同的ans就要+1,所以mark数组就用上了,ans直接+mark[sa[i]+height[i]]求值,注意之前的问题末尾2个字符前面一定要有字符,特判一下。最后ans要 long long。

//
//  main.cpp
//  B - Hesty Str1ng
//
//  Created by teddywang on 2017/4/29.
//  Copyright © 2017年 teddywang. All rights reserved.
//

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
using namespace std;
const int maxn=400010;
long long int mark[maxn];
int t1[maxn],t2[maxn],c[maxn];
bool cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int str[],int sa[],int ranks[],int height[],int n,int m)
{
    n++;
    int i,j,p,*x=t1,*y=t2;
    for(i=0;i<m;i++) c[i]=0;
    for(i=0;i<n;i++) c[x[i]=str[i]]++;
    for(i=1;i<m;i++) c[i]+=c[i-1];
    for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for(j=1;j<=n;j<<=1)
    {
        p=0;
        for(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<m;i++) c[i]=0;
        for(i=0;i<n;i++) c[x[y[i]]]++;
        for(i=1;i<m;i++) c[i]+=c[i-1];
        for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        if(p>=n) break;
        m=p;
    }
    int k=0;
    n--;
    for(i=0;i<=n;i++)ranks[sa[i]]=i;
    for(i=0;i<n;i++)
    {
        if(k)k--;
        j=sa[ranks[i]-1];
        while(str[i+k]==str[j+k])k++;
        height[ranks[i]]=k;
    }
}
int ranks[maxn],height[maxn];
char str[maxn],s1[maxn];
int r[maxn],sa[maxn];
int pre[maxn],vis[maxn];
int main()
{
    //freopen("/Users/apple/Desktop/textin.txt","r",stdin);
    //freopen("/Users/apple/Desktop/text1.txt","w",stdout);
    while(~scanf("%s",str))
    {
        int len=strlen(str);
        int n=len;
        for(int i=0;i<=len;i++) r[i]=str[i];
        r[len]=0;
        memset(vis,0,sizeof(vis));
        memset(pre,0,sizeof(pre));
        long long int ans=0;
        da(r,sa,ranks,height,n,128);
        int visit[26];
        memset(visit,0,sizeof(visit));
        memset(mark,0,sizeof(mark));
        int nums=0;
        for(int i=n-1;i>=0;i--)
        {
            if(str[i]==str[i+1])
            {
                nums++;
            }
            mark[i]=nums;
        }
        long long int tt=0;
        for(int i=1;i<=n;i++)
        {
            int buff=0;
            if(i==0) buff=1;
            else buff=max(height[i],1);
            int j=sa[i]+buff-1;
            if(j==sa[i]) j++;
            tt+=mark[j];
            visit[str[i-1]-'a']=1;
            ans+=n-sa[i]-height[i];
        }
        int buf=0;
        for(int i=0;i<26;i++)
        {
            buf+=visit[i];
        }
        ans-=buf;
        printf("%lld\n",ans+tt);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值