HDU 5769 Substring

后缀数组。

然后按照排序完成之后的顺序,每个后缀统计贡献量。

统计第i个后缀的贡献的时候,如果这个后缀中没有X,贡献度为0。

有贡献的分3种情况考虑:

1.如果这个后缀height部分等于0(即与前一个后缀没有公共前缀),那么在height之后的部分中找到第一个X的位置pos,n-pos为贡献度。

2.如果这个后缀height部分不等于0,如果这个后缀的height部分有X,那么贡献度为n-SA[i]-height[i];

3.如果这个后缀height部分不等于0,如果这个后缀的height部分没有X,那么需要在height之后的部分中找到第一个X的位置pos,n-pos为贡献度。

寻找pos的话,可以预处理前缀X个数sum[i],然后二分一下。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-8;
void File()
{
    freopen("D:\\in.txt","r",stdin);
    freopen("D:\\out.txt","w",stdout);
}
inline int read()
{
    char c = getchar();  while(!isdigit(c)) c = getchar();
    int x = 0;
    while(isdigit(c)) { x = x * 10 + c - ‘0‘; c = getchar(); }
    return x;
}

const int maxn=100000+10;

int wa[maxn],wb[maxn],wv[maxn],WS[maxn];
int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0; i<m; i++) WS[i]=0;
    for(i=0; i<n; i++) WS[x[i]=r[i]]++;
    for(i=1; i<m; i++) WS[i]+=WS[i-1];
    for(i=n-1; i>=0; i--) sa[--WS[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++) WS[i]=0;
        for(i=0; i<n; i++) WS[wv[i]]++;
        for(i=1; i<m; i++) WS[i]+=WS[i-1];
        for(i=n-1; i>=0; i--) sa[--WS[wv[i]]]=y[i];
        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;
}

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;
}

int T,n,a[maxn],SA[maxn],sum[maxn];
char str[maxn],op[5];

int Find(int D,int l,int r)
{
    int pos=-1;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(sum[mid]-D>1) r=mid-1;
        else if(sum[mid]-D==1) pos=mid,r=mid-1;
        else l=mid+1;
    }
    return pos;
}

int main()
{
    scanf("%d",&T); int cas=1;
    while(T--)
    {
        scanf("%s%s",op,str); n=strlen(str);
        for(int i=0;i<n;i++) a[i]=(int)str[i];
        a[n]=0; da(a,SA,n+1,300); calheight(a,SA,n);
        memset(sum,0,sizeof sum);
        for(int i=0;i<=n;i++)
        {
            if(i-1>=0) sum[i]=sum[i-1];
            if(a[i]==int(op[0])) sum[i]++;
        }

        LL ans=0;
        for(int i=0;i<=n;i++)
        {
            int D; if(SA[i]-1<0) D=0; else D=sum[SA[i]-1];
            if(sum[n]-D==0) continue;
            if(height[i]==0) ans=ans+n-Find(D,SA[i],n);
            else
            {
                if(sum[SA[i]+height[i]-1]-D!=0) ans=ans+n-SA[i]-height[i];
                else ans=ans+n-Find(sum[SA[i]+height[i]-1],SA[i]+height[i],n);
            }
        }
        printf("Case #%d: %lld\n",cas++,ans);
    }
    return 0;
}
时间: 2024-07-31 08:41:57

HDU 5769 Substring的相关文章

HDU 5769 Substring(后缀数组)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5769 [题目大意] 在一个串中求出包含字母的子串个数, 只要存在一个字符不相等的子串即可视为不同的子串. [题解] 因为要求存在字符不相等因此我们可以利用后缀数组统计, 后缀数组的h数组可以记录前后两个后缀的最长公共前缀这样子相同的前缀就不会被多次计算, 保存每个位置之后出现的最近的要求的字母的位置, 从该后缀的包含该字母的位置往后且不在最长公共前缀的范围内的位置都可以作为子串的右端点, 统计左

hdu 5769 Substring 后缀数组 + KMP

http://acm.hdu.edu.cn/showproblem.php?pid=5769 题意:在S串中找出X串出现的不同子串的数目? 其中1 官方题解: 处理出后缀数组中的sa[]数组和height[]数组.在不考虑包含字符X的情况下,不同子串的个数为 如果要求字符X,只需要记录距离sa[i]最近的字符X的位置(用nxt[sa[i]]表示)即可,个数 理解:后缀数组height[i]就是sa[i]与sa[i-1]的LCP,在后缀数组中求解全部的不同子串(之前只写过SAM处理所有不同子串..

HDU 3925 Substring 【大数相减】

题目意思是,给你提供两个数字 a 和 b a 可以不断的往上加, 直到b 为其子串 问的是 a 最小加几? 显而易见,a  的数据范围给了10 ^100非常大,直接模拟肯定不行 那么就用 b 减去 a 来找,也算是一种模拟的方法 举个例子, a = 1299, b = 33 <1>33 330 3300 33000 (12)99 (1)299 1299  1299 ------ -------- --------     ------- 34 31 2001 32001 如果当前 b 比a的部

HDU 1403 Longest Common Substring(后缀数组,最长公共子串)

hdu题目 poj题目 参考了 罗穗骞的论文<后缀数组——处理字符串的有力工具> 题意:求两个序列的最长公共子串 思路:后缀数组经典题目之一(模版题) //后缀数组sa:将s的n个后缀从小到大排序后将 排序后的后缀的开头位置 顺次放入sa中,则sa[i]储存的是排第i大的后缀的开头位置.简单的记忆就是“排第几的是谁”. //名次数组rank:rank[i]保存的是suffix(i){后缀}在所有后缀中从小到大排列的名次.则 若 sa[i]=j,则 rank[j]=i.简单的记忆就是“你排第几”

HDU 4426 Palindromic Substring

Palindromic Substring Time Limit: 10000ms Memory Limit: 65536KB This problem will be judged on HDU. Original ID: 442664-bit integer IO format: %I64d      Java class name: Main In the kingdom of string, people like palindromic strings very much. They

hdu 5677 ztr loves substring 二维费用背包+回文

题目链接: hdu 5677 ztr loves substring 官方题解: //这部分是错的(首先,对于每一个串i跑一次manancher,令g[i][j]=p[j]-1g[i][j]=p[j]−1 这样,g就存储了所有的回文子串的长度 为了方便,把g降到一维表示) 首先,因为字符串长度较小,所以直接二重for循环找就好了,用一个数组 g记录各个回文串的长度 不妨把每一个子串抽象成一个物品 费用为二维的,即{长度,1} 价值是Bool型的 这样就成了一个二维判断可行性费用背包问题 设f(i

hdu 2459 Maximum repetition substring(后缀数组)

题目链接:hdu 2459 Maximum repetition substring 题意: 让你找一个重复最多的子串,并且输出. 题解: 这个是论文题,看的cxlove的题解,不是很理解为什么这样就能完全找完,当作结论使吧. 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespace std; 4 namespace suffixarray{ 5 #define FN(n) f

HDU 1403 Longest Common Substring(后缀数组啊 求最长公共子串 模板题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1403 Problem Description Given two strings, you have to tell the length of the Longest Common Substring of them. For example: str1 = banana str2 = cianaic So the Longest Common Substring is "ana", a

HDU - 1403 - Longest Common Substring

先上题目: Longest Common Substring Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4010    Accepted Submission(s): 1510 Problem Description Given two strings, you have to tell the length of the Long