【spoj705】 Distinct Substrings

【题目描述】

给定一个字符串,计算其不同的子串个数。

【输入格式】

一行一个仅包含大写字母的字符串,长度<=50000

【输出格式】

一行一个正整数,即不同的子串个数。

【样例输入】

ABABA

【样例输出】

9

【思路】  一看就知道是后缀数组题啦~但是我不会写QAQ。。只好现学现用啦~  在字符串最后补上一个‘$‘,不因为别的只因为它比‘A’还要小。。不然你补ascII码是0的也可以。。  申请rank数组和sa数组,rank[i]=j代表后缀i排第j位,sa[i]=j代表排名第i的是后缀j。也就是说rank和sa是相反的运算。  首先将sa数组按照单字母的顺序排个序,更新rank数组,不过记得字母相同的排名也要相同,也就是如果str[sa[i]]==str[sa[i-1]]的话rank[sa[i]]=rank[sa[i-1]];else rank[sa[i]]=rank[sa[i-1]]+1;  然后将k从0开始枚举,每次继续对sa进行排序,但是是以rank[sa[i]]为第一关键字,rank[sa[i]+2k]为第二关键字排序。  在Trank中(其实就是tmpRank)更新好新的rank值,一样记得如果rank[sa[i]]==rank[sa[i-1]]&&rank[sa[i]+2k]==rank[sa[i-1]+2k]的话排名不要上升。  重复这一步,直到2k>=n或者所有后缀的排名都不同。  然后正常情况下k增加logN次,每次如果用计数排序只要O(N),一共O(NlogN)。  但是不会写计数排序啊QAQ。。所以用快排好了。。多加一个log,一般不会被卡的吧。。  计算出来sa和rank之后还要计算height数组,height[i]代表sa[i]和sa[i-1]的最长公共前缀,如果按照1——n的顺序计算的话是O(N2)的,显然不够优秀,于是我们按照一种奇怪的顺序计算。  先算height[rank[1]],然后是height[rank[2]]……  这样的话就会有一个性质:height[rank[i]]>=height[rank[i-1]]-1  我也不知道为什么但是就是这样的。。  然后就没有然后了。。  我们知道任何一个子串都是某一个后缀的一个前缀  对于后缀i来说,有length-i个前缀,其中有height[i]个和前一个后缀相同  所以答案就是Σlength-i-height[i]

 1 #include <iostream>
 2 #include <cstring>
 3 #include <string>
 4 #include <cstdio>
 5 #include <cstdlib>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <queue>
 9 #include <stack>
10 #include <map>
11 #include <set>
12 #include <list>
13 #include <vector>
14 #include <ctime>
15 #include <functional>
16 #define pritnf printf
17 #define scafn scanf
18 #define sacnf scanf
19 #define For(i,j,k) for(int i=(j);i<=(k);(i)++)
20 #define Clear(a) memset(a,0,sizeof(a))
21 using namespace std;
22 typedef unsigned int Uint;
23 const int INF=0x3fffffff;
24 const double eps=1e-10;
25 ///==============struct declaration==============
26
27 ///==============var declaration=================
28 const int MAXN=50010;
29 int n,k;
30 int sa[MAXN],rank[MAXN],h[MAXN],trank[MAXN],height[MAXN];
31 char str[MAXN];
32 ///==============function declaration============
33 bool cmp(int a,int b){return rank[a]==rank[b]?rank[a+(1<<k)]<rank[b+(1<<k)]:rank[a]<rank[b];}
34 bool cmp1(int a,int b){return str[a]<str[b];}
35 ///==============main code=======================
36 int main()
37 {
38     scanf("%s",str+1);
39     n=strlen(str+1);str[n+1]=‘$‘;n++;
40     for(int i=0;i<=n;i++)
41         sa[i]=i;
42     sort(sa+1,sa+1+n,cmp1);
43     rank[sa[1]]=1;
44     for(int i=2;i<=n;i++)
45         if (str[sa[i]]!=str[sa[i-1]])
46             rank[sa[i]]=rank[sa[i-1]]+1;
47         else
48             rank[sa[i]]=rank[sa[i-1]];
49     for(k=0;(1<<k)<=n;k++){
50         sort(sa+1,sa+1+n,cmp);
51         trank[sa[1]]=1;
52         for(int i=2;i<=n;i++){
53             if (rank[sa[i]]!=rank[sa[i-1]]||rank[sa[i]+(1<<k)]!=rank[sa[i-1]+(1<<k)])
54                 trank[sa[i]]=trank[sa[i-1]]+1;
55             else
56                 trank[sa[i]]=trank[sa[i-1]];
57         }
58         for(int i=1;i<=n;i++)
59             rank[i]=trank[i];
60         if (rank[sa[n]]==n) break;
61     }
62     for(int i=1;i<=n;i++)
63         rank[sa[i]]=i;
64     ///height[i]表示sa[rank[i]]和sa[rank[i-1]]的最长前缀
65     height[rank[1]]=0;
66     for(int i=1;i<=n;i++){
67         height[rank[i]]=max(height[rank[i-1]]-1,0);
68         int p=i,q=sa[rank[i]-1];
69         while (str[p+height[rank[i]]]==str[q+height[rank[i]]])
70             height[rank[i]]++;
71     }
72     long long ans=0;
73     for(int i=1;i<=n;i++)
74         ans+=n-sa[i]-height[i];
75       printf("%lld\n",ans);
76    return 0;
77 }
78 ///================fuction code====================

Spoj 705

   尽量多做后缀数组的题目吧,熟能生巧,现在只是刚刚学会了后缀数组,还要多多加油才是。

 
时间: 2024-10-13 22:27:11

【spoj705】 Distinct Substrings的相关文章

【SPOJ】Distinct Substrings(后缀自动机)

[SPOJ]Distinct Substrings(后缀自动机) 题面 Vjudge 题意:求一个串的不同子串的数量 题解 对于这个串构建后缀自动机之后 我们知道每个串出现的次数就是\(right/endpos\)集合的大小 但是实际上我们没有任何必要减去不合法的数量 我们只需要累加每个节点表示的合法子串的数量即可 这个值等于\(longest-shortest+1=longest-parent.longest\) #include<iostream> #include<cstdio&g

【SPOJ694】Distinct Substrings (SA)

求不相同子串个数    该问题等价于求所有后缀间不相同前缀的个数..也就是对于每个后缀suffix(sa[i]),将贡献出n-sa[i]+1个,但同时,要减去那些重复的,即为height[i],故答案为n-sa[i]+1-height[i]的累计. const maxn=1419; var x,y,rank,sa,h,c:array[0..maxn] of longint; s:ansistring; t,q,n:longint; function max(x,y:longint):longin

【leetcode】Distinct Subsequences

问题:给定两个字符串S,T,对于S,可以删除其中的任意多个(包括0)字符,使其得到T.问有多少种删法可以得到T. 举例分析: S:ababa T: aba dp[i][j] : 表示 S 从0 ~ i - 1,T从0~j - 1,所得到的方法数.i,j 表示长度. 初始条件:dp[i][0] = 1,T为空串,而空串总是任意串的字串.即,将S串的所有字符都删掉,就得到T. 状态转移方程: dp[ i ] [ j ] = dp[ i - 1] [ j - 1] + dp[ i - 1] [ j ]

【CF316G3】Good Substrings 后缀自动机

[CF316G3]Good Substrings 题意:给出n个限制(p,l,r),我们称一个字符串满足一个限制当且仅当这个字符串在p中的出现次数在[l,r]之间.现在想问你S的所有本质不同的子串中,有多少个满足所有限制. |S|,|p|<=10^5,n<=10. 题解:比较简单的后缀自动机题,我们先把原串和所有限制串放到一起建一个广义后缀自动机,然后在pre树上统计一下即可得到每个子串在每个限制串中出现了多少次.现在我们想知道原串中有多少满足条件的子串,即我们统计一下所有出现次数符合要求的,

【LeetCode】Distinct Subsequences 解题报告

[题目] Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the rela

【SPOJ694&amp;705】Distinct Substrings(后缀数组)

题意:求一个字符串的不相同的子串个数 n<=1000 思路:这是一道论文题 1 var a,x,y,sa,rank,height,wc,wd:array[0..3000]of longint; 2 n,i,m,ans,v,cas:longint; 3 ch:ansistring; 4 5 procedure swap(var x,y:longint); 6 var t:longint; 7 begin 8 t:=x; x:=y; y:=t; 9 end; 10 11 function cmp(a

【SPOJ 694】Distinct Substrings 不相同的子串的个数

不会FQ啊,没法评测啊,先存一下代码QAQ #include<cstdio> #include<cstring> #include<algorithm> const int N = 1003; int t1[N], t2[N], c[N], rank[N], a[N], sa[N], h[N]; void st(int *x, int *y, int *sa, int n, int m) { int i; for(i = 0; i < m; ++i) c[i] =

【HDOJ】4455 Substrings

5000ms的时限,还挺长的.算法是DP.思路是找到ans[1..n]的结果,然后Query就容易做了.问题是怎么DP?考虑:1 1 2 3 4 4 5w=1: 7, 7 = 1 * 7w=2: 10,10 = |{1,1}|+|{1,2}|+|{2,3}|+|{3,4}|+|{4,4}|+|{4,5}|=1+2+2+2+1+2=10w=3: 12,  12 = |{1,1, 2}|+|{1,2, 3}|+|{2,3, 4}|+|{3,4, 5}|+|{4,4, 5}|...观察w=3如何在w=

【SPOJ】8222. Substrings(后缀自动机)

http://www.spoj.com/problems/NSUBSTR/ 题意:给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值.求F(1)..F(Length(S)) 这题做法: 首先建立字符串的后缀自动机. 因为自动机中的每个状态都代表一类子串前缀,且任意状态的最长的|max|所对应的子串是唯一的. 所以我们算出每个子串(即找到的状态是end态),他们的right值为++(即保证长度为当前子串的出现次数为1),然后自底向上在parent树中更新right值(pare