Boring counting HDU - 3518 (后缀数组)

Boring counting

\[
Time Limit: 1000 ms \quad Memory Limit: 32768 kB
\]

题意

给出一个字符串,求出其中出现两次及以上的子串个数,要求子串之间不可以重合。

思路

对字符串后缀数组,然后枚举子串长度 \(len\),若某一段连续的 \(sa[i]\) 的 \(lcp \geq len\),那么说明这一段内存在一个长度为 \(lcp\) 的子串,而我们只需要其中的前 \(len\) 部分,接下来只要找出这个子串出现的最左和最右位置,然后判断中间能否放下这个长度为 \(len\) 的子串就可以了,如果可以的话,就让 \(ans\)++。



\(Hint\)
这题还让我学到后缀数组的另一个细节,在构建后缀数组之前,要在末尾加上一个比所有字符都小的字符,因为在求 \(height\) 的时候需要判断 \(a[i+k]==a[j+k]\),否则这里可能会无限扩展下去。

/***************************************************************
    > File Name    : a.cpp
    > Author       : Jiaaaaaaaqi
    > Created Time : 2019年05月23日 星期四 22时40分17秒
 ***************************************************************/

#include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <cfloat>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define  lowbit(x)  x & (-x)
#define  mes(a, b)  memset(a, b, sizeof a)
#define  fi         first
#define  se         second
#define  pii        pair<int, int>

typedef unsigned long long int ull;
typedef long long int ll;
const int    maxn = 1e5 + 10;
const int    maxm = 1e5 + 10;
const ll     mod  = 1e9 + 7;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;
const double pi   = acos(-1.0);
const double eps  = 1e-8;
using namespace std;

int n, m;
int cas, tol, T;

char s[maxn];
int a[maxn], sa[maxn], tp[maxn], tax[maxn], rk[maxn], height[maxn];

void rsort(int n, int m) {
    for(int i=0; i<=m; i++) tax[i] = 0;
    for(int i=1; i<=n; i++) tax[rk[tp[i]]]++;
    for(int i=1; i<=m; i++) tax[i] += tax[i-1];
    for(int i=n; i>=1; i--) sa[tax[rk[tp[i]]]--] = tp[i];
}

int cmp(int *f, int x, int y, int w) {
    return f[x]==f[y] && f[x+w]==f[y+w];
}

void SA(int *a, int n, int m) {
    for(int i=1; i<=n; i++) rk[i] = a[i], tp[i] = i;
    rsort(n, m);
    for(int w=1, p=1, i; p<n; w<<=1, m=p) {
        for(p=0, i=n-w+1; i<=n; i++)    tp[++p] = i;
        for(i=1; i<=n; i++) if(sa[i] > w)   tp[++p] = sa[i]-w;
        rsort(n, m), swap(rk, tp);
        rk[sa[1]] = p = 1;
        for(i=2; i<=n; i++) rk[sa[i]] = cmp(tp, sa[i], sa[i-1], w) ? p : ++p;
    }
    int j, k=0;
    for(int i=1; i<=n; height[rk[i++]] = k)
        for(k = k ? k-1 : k, j=sa[rk[i]-1]; a[i+k]==a[j+k]; k++);
}

int calc(int len) {
    int ans = 0;
    int mx, mn;
    mx = -inf, mn = inf;
    for(int i=2; i<=n; i++) {
        if(height[i]>=len) {
            mx = max(mx, max(sa[i], sa[i-1]));
            mn = min(mn, min(sa[i], sa[i-1]));
        } else {
            if(mx - mn >= len)  ans++;
            mx = -inf, mn = inf;
        }
    }
    if(mx - mn >= len)  ans++;
    return ans;
}

int main() {
    while(scanf("%s", s+1)) {
        if(s[1] == '#') break;
        n = strlen(s+1);
        s[++n] = 2;
        for(int i=1; i<=n; i++) {
            a[i] = s[i];
        }
        SA(a, n, 260);
        // for(int i=1; i<=n; i++) {
        //     printf("sa[%d] = %d\n", i, sa[i]);
        // }
        int ans = 0;
        for(int i=1; i<=(n+1)/2; i++) {
            ans += calc(i);
        }
        printf("%d\n", ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Jiaaaaaaaqi/p/10915328.html

时间: 2024-09-30 00:10:54

Boring counting HDU - 3518 (后缀数组)的相关文章

hdu 3518 后缀数组

Boring counting Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 2906    Accepted Submission(s): 1201 Problem Description 035 now faced a tough problem,his english teacher gives him a string,whic

hdu 3948 后缀数组

The Number of Palindromes Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 2465    Accepted Submission(s): 841 Problem Description Now, you are given a string S. We want to know how many distin

Hdu 1403(后缀数组)

题目链接 Longest Common Substring Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4077    Accepted Submission(s): 1544 Problem Description Given two strings, you have to tell the length of the Longe

HDU5008 Boring String Problem(后缀数组 + 二分 + 线段树)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5008 Description In this problem, you are given a string s and q queries. For each query, you should answer that when all distinct substrings of string s were sorted lexicographically, which one is

HDU - 3948 后缀数组+Manacher

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3948 题意:给定一个字符串,求字符串本质不同的回文子串个数. 思路:主要参考该篇解题报告 先按照manacher的构造方法改造一遍串,然后跑一遍manacher.求出以i为中心的最长回文串长度p[i]. 然后跑一遍后缀数组,若已经求得后缀sa[i-1]对答案的贡献,然后现在计算后缀sa[i],本来是要加上以sa[i]为中心的回文串的个数p[sa[i]]. 我们可以维护一个tmp,也就是上图中蓝色的框

HDU 6194 后缀数组

题意:求出现恰好 k次的子串(可以重叠)的个数: 分析: 刚开始想到了是后缀数组,但是有什么性质,具体怎么做的没有想到.回到主题来: 连续 k 次,说明这 k 个后缀排序后在一起,每次枚举 长度的为 k 的区间,用RMQ算出最长公共前缀长度,这里就有 len 个子串是 符合满足 k 次的,但是又有可能过短而不止出现了 k次,那么有多少呢? 那么就是 a 不属于,len - height[j+1],前面也一样.也就是每次只算他独有的子串,但是你可能问 len > k 的时候,可以从新组成一组子串!

HDU 4358 Boring counting(树状数组)

题意:  给定一棵树,每个节点有一个点权,然后有一些询问,求以某个点为根的子树中有多少的数出现了恰好k次. 思路: 首先dfs一次将树形结构转化成线性结构,利用时间戳记录下以结点u为根的子树在数组中的开始位置和结束位置. 那么我们将所有查询记录下来离线来做,将所有的查询按右端点升序排序. 考虑用树状数组来做这道题,每个位置记录当前从1到当前位置有多少数出现了恰好k次. 从头遍历一遍数组,map离散化记录每个值出现的位置,对于每个位置,如果值出现的次数t大于k,那么在将第t-k次出现的位置加一

HDU - 1403 后缀数组初步

题意:求两个串的最长公共子串 两个串连接起来然后求高度数组 注意两个sa值必须分别在不同一侧 本题是用来测试模板的,回想起青岛那次翻车感觉很糟糕 #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<string> #include<vec

后缀数组 --- HDU 3518 Boring counting

Boring counting Problem's Link:   http://acm.hdu.edu.cn/showproblem.php?pid=3518 Mean: 给你一个字符串,让你求出有多少个子串(无重叠)至少出现了两次. analyse: 后缀数组中height数组的运用,一般这个数组用得很少. 总体思路:分组统计的思想:将相同前缀的后缀分在一个组,然后对于1到len/2的每一个固定长度进行统计ans. 首先我们先求一遍后缀数组,并把height数组求出来.height数组代表的