codeforces 432D D . Prefixes and Suffixes(后缀数组)

题目链接:

codeforces 432D


题目大意:

给出一个字符串,求有多少种长度的前缀和后缀相等,并且得到的这个子串在原字符串中出现的次数。


题目分析:

  • 首先利用后缀数组处理出sa[i]代表排名第i位的后缀的起始位置
  • 处理出rank[i]代表第i个位置起始的后缀的rank
  • 处理出height[i]代表排名第i位的和排名i-1位的公共前缀的长度。
  • 那么我们要找后缀和前缀相等的就是找到rank[0],然后按照排名,向前向后遍历,任意两个后缀的公共前缀就是他们[i,j]区间内所有height的最小值。只要得到的公共前缀等于后缀的长度,那么证明前缀和后缀匹配上。记录该长度的前缀能够匹配
  • 记录某一个子串出现的次数,就是每次记录和原字符串的公共前缀的长度,然后在对应位置+1,因为比这个长度小的也会得到一个贡献,所以最后求一下后缀和即可。

AC代码:

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define MAX 100007

using namespace std;

char s[MAX];
int r[MAX],sa[MAX],wa[MAX],wb[MAX],wv[MAX],wss[MAX],rank[MAX],height[MAX];
int cnt[MAX];
bool mark[MAX];

int cmp ( int *r , int a , int b , int len )
{
    return r[a]==r[b]&&r[a+len]==r[b+len];
}  

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++ ) wss[i] = 0;
    for ( i = 0 ; i < n ; i++ ) wss[x[i] = r[i]]++;
    for ( i = 0 ; i < m ; i++ ) wss[i] += wss[i-1];
    for ( i = n-1 ; i >= 0 ; i-- ) sa[--wss[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++ ) wss[i] = 0;
        for ( i = 0 ; i < n ; i++ ) wss[wv[i]]++;
        for ( i = 0 ; i < m ; i++ ) wss[i] += wss[i-1];
        for ( i = n-1 ; i >= 0 ; i-- ) sa[--wss[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++;
    }
}  

void calheight ( int *r , int *sa , int n )
{
    int i , j , k = 0;
    for ( int i = 1 ; i <= n ; i++ ) rank[sa[i]] = i;
    for ( int i = 0 ; i < n ; height[rank[i++]] = k )
        for ( k?k--:0 , j = sa[rank[i]-1] ; r[i+k] == r[j+k] ; k++ );
}

int main ( )
{
    while ( ~scanf ( "%s" , s ) )
    {
        memset ( cnt , 0 , sizeof ( cnt ) );
        memset ( mark , 0 , sizeof ( mark ) );
        int len = strlen ( s );
        for ( int i = 0 ; i < len ; i++ )
            r[i] = s[i] - ‘A‘ + 1;
        da ( r , sa , len+1 , 27 );
        calheight ( r , sa, len );
        int x = rank[0];
        int temp = 1<<29;
        for ( int i = x + 1 ; i <= len;i ++ )
        {
            temp = min ( temp , height[i] );
            int id = sa[i];
            if ( sa[i]+temp >= len )
                mark[temp] = true;
            cnt[temp]++;
        }
        temp = height[x];
        for ( int i = x-1 ; i >= 1; i -- )
        {
            cnt[temp]++;
            int id = sa[i];
            if ( sa[i]+temp >= len )
                mark[temp] = true;
            temp = min ( temp , height[i] );
        }
        mark[len] = true;
        cnt[len] = 1;
        int ans = 0;
        for ( int i = len-1 ;i > 0 ; i-- )
            cnt[i] += cnt[i+1];
        for ( int i = 1 ; i <= len ; i ++ )
            if ( mark[i] )  ans++;
        printf ( "%d\n" , ans );
        for  ( int i = 1 ; i <= len ; i++ )
        {
            if ( !mark[i] ) continue;
            printf ( "%d %d\n" , i , cnt[i] );
        }
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-22 01:33:18

codeforces 432D D . Prefixes and Suffixes(后缀数组)的相关文章

codeforces Round 246 D. Prefixes and Suffixes (后缀数组 || KMP)

题目大意: 求一个子串,子串既是前缀又是后缀. 然后再求出它在整个串中出现的次数. 思路分析: 可以很容易想到如何判断一个串既是前缀又是后缀. 只需要它与 sa[0] 的lcp 等于 整个串的长度减去它的 sa 值. 然后接下来的工作是判断出现了 多少次. 首先我们想到,如果这个子串是目标前后缀. 那么出现过它的子串在sa 中的下标一定比这个串大. 因为它已经是最简的了. 然后可以直接二分求出他出现了多少次. #include <iostream> #include <cstdio>

Codeforces Round #246 (Div. 2) D. Prefixes and Suffixe 后缀数组

链接: codeforces.com/contest/432/problem/D 题意: 给你一个字符串,求每一个和前缀匹配的后缀在这个字符串中出现的次数 题解: 先算出lcp,找到sa[i]==0的位置标记为beg,和前缀匹配的后缀一定会出现beg的左边,这个想一想明白了 我们先算出beg左边每一个后缀和beg匹配的长度,beg右边的先放到一个vector中,便于之后二分查找 我们从beg向左遍历,对于每一个dp[i],如果dp[i]==n-sa[i],就可以更新结果了,从i到beg中间的都满

Codeforces 432D Prefixes and Suffixes (KMP、后缀数组)

题目链接: https://codeforces.com/contest/432/problem/D 题解L 做法一: KMP 显然next树上\(n\)的所有祖先都是答案,出现次数为next树子树大小. 做法二: 后缀数组 按照height分组,二分查找即可. 代码 KMP: #include<cstdio> #include<cstdlib> #include<cstring> #include<vector> #include<utility&g

Codeforces 432D Prefixes and Suffixes(KMP+dp)

题目连接:Codeforces 432D Prefixes and Suffixes 题目大意:给出一个字符串,求全部既是前缀串又是后缀串的字符串出现了几次. 解题思路:依据性质能够依据KMP算法求出全部的前后缀串,然后利用dp求解,dp[i]表示从1到i这个子串出现过的次数.转移方程dp[jump[i]]+=dp[i].随意一个dp[i]的初始状态应该是1. #include <cstdio> #include <cstring> const int N = 1e5+5; int

CodeForces Round #527 (Div3) C. Prefixes and Suffixes

http://codeforces.com/contest/1092/problem/C Ivan wants to play a game with you. He picked some string ss of length nn consisting only of lowercase Latin letters. You don't know this string. Ivan has informed you about all its improper prefixes and s

Codeforces Round #422 (Div. 2) E. Liar 后缀数组+RMQ+DP

E. Liar The first semester ended. You know, after the end of the first semester the holidays begin. On holidays Noora decided to return to Vi?kopolis. As a modest souvenir for Leha, she brought a sausage of length m from Pavlopolis. Everyone knows th

codeforces 427D Match &amp; Catch(后缀数组,字符串)

题目 参考:http://blog.csdn.net/xiefubao/article/details/24934617 题意:给两个字符串,求一个最短的子串.使得这个子串在两个字符串中出现的次数都等于1.出现的定义为:可以重叠的出现. 解法:后缀数组的应用.从小枚举长度.如果一个长度len合法的话:则一定存在这个样的sa[i]排名.sa[i]与s[i+1]的公共前缀长度大于等于len,且sa[i]与[i-1]的公共前缀长度小于len,同时sa[i+1]与[i+2]的公共前缀长度小于len,同时

Codeforces VK Cup 2015 A.And Yet Another Bracket Sequence(后缀数组+平衡树+字符串)

这题做得比较复杂..应该有更好的做法 题目大意: 有一个括号序列,可以对其进行两种操作: ·        向里面加一个括号,可以在开头,在结尾,在两个括号之间加. ·        对当前括号序列进行循环移动,即把最后一个括号拿到开头来. 上述两种操作可以做任意次,要求添加最少的括号使得原序列变成一个合法括号序列.如果有多种可能,输出字典序最小的那一个."(" < ")". 题解: 首先计算左括号和右括号的数量,可以知道,不妨假设左括号的数量大于右括号 那么

CodeForces 873F Forbidden Indices 后缀数组

忘了当时怎么做的了,先把代码贴上,保存一下后缀数组模板. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define REP(i, a, b) for(int i = a; i < b; i++) #define PER(i, a, b) for(int i = b - 1; i >= a; i--) typedef long long LL;