后缀数组

关于KMP算法

有些话、适合烂在心里 提交于 2020-02-07 09:26:48
KMP 算法是非常经典的字符串匹配算法,而且有可能是最经典的一个。同时它也是非常典型的一种优化算法,它把原本暴力法 O( mn ) 的最坏复杂度降低到了 O( m+n )(虽然实际上暴力法的执行复杂度期望依然是线性的),其思想非常具有典型性和可借鉴性,值得好好学习。 1 基本思想 KMP 算法的基本思想是,借助一个预先计算好的数组 pi ,在匹配了一定数量的模式的情况下,遇到不匹配的字符时,不像暴力法那样将待匹配的文本下标从上一次匹配的地方向后移动一个位置,并将已匹配的模式个数清零,而是利用已经匹配部分的信息,迅速跳过那些不可能匹配成功的文本开头,减少了暴力法中一些不必要的盲目搜索。 比如,对于模式 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 a b a b a b c a b c a b c d b c d b c d 如果从文本中的某一个字符开始,匹配到6号字符c时,出现了不匹配,此时已经匹配了6个字符。如果再从文本中下一个字符b开始,从模式的第0号字符开始继续尝试,就是暴力法的思想。而 KMP 算法比暴力法先进的地方就在于,它利用 模式 已匹配部分的信息,跳过那些已遍历的文本中不可能匹配的开头,直接进入有可能会产生匹配的文本开头,并更新相应的已匹配字符长度。比如,匹配到6号字符时,出现不匹配

algorithm@ KMP

北战南征 提交于 2020-02-07 09:21:39
一. KMP算法 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,简称KMP算法。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。 二. KMP算法的意义 先举一个简单模式匹配的例子,给定字符串T=“abababca”,S=“bacbababaabcbab”,判断T是否是S的子串,如果用暴力扫描的话,就是拿着T字符串从S的头扫到尾。这样的时间复杂度最坏情况下是O(n*m),其中n和m分别是主串和模式串的长度。而KMP算法的时间消耗是O(n+m)的,至于为什么这样,下面再说。 三. KMP算法的核心 KMP的核心就是一张表,我们称之为部分匹配表,起初我看这张表的时候也是云山雾绕,不知所云,部分匹配表是为模式串T专门设计的,T中每个字符对应着一个整数值,(这个地方也是困扰了我很久),现在我尽可能说得明白一些。首先下面附上一张T为“abababca”的部分匹配图,让大家“先睹为快”,看看部分匹配表是个什么东东。 好了,现在我们有了一个含有8个字符的模式串T,那么最后一行的value值是怎么得来的呢?别急,我先介绍相关概念:前缀和后缀,就拿字符串”abca“来说,”abca“的前缀有{a,ab,abc},

KMP算法

你。 提交于 2020-02-07 09:18:37
这篇文章想简单讲讲 KMP 算法的内容。 KMP 算法 KMP 算法由 Knuth–Morris–Pratt 三个人共同提出,它的目的是判断字符串 A 中是否包含另一个字符串 B(如:判断 abababaababacb 中是否包含 ababacb )。 KMP 算法流程 KMP 下面演示一下 KMP 的流程。假设我们要判断字符串 A(abababaababacb)中是否包含字符串 B(ababacb)。 我们分别用两个指针 i 和 j 指示 A、B 匹配的位置。 首先比较第一个位置: i: 0 A: a B: a j: 0 匹配了 a 跟 a,向前移动指针 i 和 j : i: 01 A: ab B: ab j: 01 匹配了 b,继续向前移动指针 i 、 j ,直到: i: 01234 5 A: ababa b B: ababa c j: 01234 5 按照常规的方法,我们要把 i 从上次起始点 0 移动到 1,而 j 则回到 0 继续匹配。但你是否注意到一个现象:我们已经用 B 的 ababa 匹配了 A 的 ababa,也就是说,我们已经掌握 A、B 前面这部分的信息,那么,对于前面这部分信息能否相互匹配,我们其实已经知道了。 比如说,我们没有必要把 i 和 j 重新调回 1 和 0,因为 A[1] 和 B[0] 肯定是不匹配的。最明智的做法是调成下面这种状态: i:

CodeForces.1174D.ArraySplitting.(后缀和数组 + 贪心)

橙三吉。 提交于 2020-02-04 13:18:34
   题目链接 参考代码: #include <cstdio> #include <algorithm> using namespace std; typedef long long ll; const int maxn = 300000 + 5; int n, k; ll ans = 0, value[maxn]; bool cmp(const ll &a, const ll &b) { return a > b; } int main() { scanf("%d %d", &n, &k); for(int i = 1; i <= n; i ++) scanf("%I64d", &value[i]); for(int i = n; i; i --) value[i] += value[i + 1];//构造value后n项和的后缀和数组 sort(value + 2, value + n + 1, cmp);//对后n - 1项进行排序,因为第后n项和是必须要加的 for(int i = 1; i <= k; i ++) ans += value[i];//选取其中最大的n - 1项和第一项即可,就可以实现将所选的数字加k次 printf("%I64d\n", ans); return 0; } 来源: https://www.cnblogs.com/bianjunting/p

数据结构之后缀数组suffix array

谁说我不能喝 提交于 2020-02-03 00:50:00
在字符串处理当中,后缀树和后缀数组都是非常有力的工具,其中后缀树大家了解得比较多,关于后缀数组则很少见于国内的资料。其实后缀是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现,能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多。可以说,在信息学竞赛中后缀数组比后缀树要更为实用。因此在本文中笔者想介绍一下后缀数组的基本概念、构造方法,以及配合后缀数组的最长公共前缀数组的构造方法,最后结合一些例子谈谈后缀数组的应用。 基本定义: 子串 字符串 S 的子串 r[i..j] , i ≤ j ,表示 r 串中从 i 到 j 这一段,就是顺次排列 r[i],r[i+1],...,r[j] 形成的字符串。 后缀 后缀是指从某个位置 i 开始到整个串末尾结束的一个特殊子串。字符串 r 的从 第 i 个字符开始的后缀表示为 Suffix(i) ,也就是Suffix(i)=r[i..len(r)] 。 大小比较 大小比较:关于字符串的大小比较,是指通常所说的“字典顺序”比较,也就是对于两个字符串u、v,令i 从1 开始顺次比较u[i]和v[i],如果u[i]=v[i]则令 i 加1,否则若u[i]<v[i]则认为u<v,u[i]>v[i]则认为u>v(也就是v<u),比较结束。如果i>len(u)或者i>len(v)仍比较不出结果,那么若len(u)<len(v)

2017年 1月 15日 后缀数组 学习整理

和自甴很熟 提交于 2020-02-03 00:48:31
写在前面 在字符串处理当中,后缀树和后缀数组都是非常有力的工具。 其中后缀树大家了解得比较多,关于后缀数组则很少见于国内的资料。 其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现, 能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多。 可以说,在信息学竞赛中后缀数组比后缀树要更为实用! 因此在本文中笔者想介绍一下后缀数组的基本概念、构造方法, 以及配合后缀数组的最长公共前缀数组的构造方法,最后结合一些例子谈谈后缀数组的应用。 一 . 学习后缀数组需要认识几个概念: 子串   字符串S的子串r[i..j],i<=j,表示S串中从i到j这一段,就是顺次排列r[i],r[i+1],...,r[j]形成的子串。 后缀   后缀是指从某个位置 i 开始到整个串末尾结束的一个特殊子串。字符串r的从第i个字符开始的后缀表示为Suffix(i), 也就是Suffix(i)=S[i...len(S)-1] 。 后缀数组 (SA[i]存放排名第i大的后缀首字符下标)   后缀数组 SA 是一个一维数组,它保存1..n 的某个排列SA[1] ,SA[2] ,...,SA[n] ,   并且保证Suffix(SA[i])<Suffix(SA[i+1]), 1<=i<n 。 也就是将S的n个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入SA 中。

后缀数组

时间秒杀一切 提交于 2020-02-03 00:46:31
什么是后缀数组 后缀树(Suffix tree)是一种数据结构,能快速解决很多关于字符串的问题,缺点是算法复杂难懂且容易出错。 而后缀数组、后缀自动机、后缀仙人掌都是后缀树的替代品。 后缀数组 Suffix Array 是一个一维数组,它将字符串S的n个后缀从小到大排序后把排好序的后缀的开头位置顺次放入数组中。 它可以由倍增算法在O(nlogn)的时间内构造出来。 后缀数组的基本概念 子串 字符串 S 的子串 r[i..j],i≤j,表示 r 串中从 i 到 j 这一段,也就是顺次排列 r[i],r[i+1],...,r[j]形成的字符串。 后缀 后缀是指从某个位置 i 开始到整个串末尾结束的一个特殊子串。 字符串 r 的从第 i 个字符开始的后缀表示为 Suffix(i),也就是 Suffix(i) = r[i..len(r)]。 字符串的大小比较 关于字符串的大小比较,是指通常所说的 “ 字典顺序 ” 比较。 也就是对于两个字符串 u 、v ,令 i 从 1 开始顺次比较 u[i] 和 v[i] ,如果u[i]=v[i] 则令 i 加 1 ,否则若 u[i]<v[i] 则认为 u<v ,u[i]>v[i] 则认为 u>v,比较结束。 如果 i>len(u) 或者 i>len(v) 仍比较不出结果,那么若 len(u)<len(v)则认为 u<v , 若 len(u)=len(v

后缀数组:倍增法和DC3的简单理解

我只是一个虾纸丫 提交于 2020-02-03 00:45:02
一些定义:设字符串S的长度为n,S[0~n-1]。 子串:设0<=i<=j<=n-1,那么由S的第i到第j个字符组成的串为它的子串S[i,j]。 后缀:设0<=i<=n-1,那么子串S[i,n-1]称作它的后缀,用Suffix[i]表示。 串比较:对于两个串S1,S2,设长度分别为n1,n2。若存在一个位置i,使得对于0<=j<i满足S1[j]=S2[j]且S1[i]<S2[i],那么我们称S1<S2。如果S1是S2的一个前缀,那么也有S1<S2。两个串相等当且仅当长度相同且所有位置的字母都相同。所以,对于S的任意两个不同的后缀Suffix[i],Suffix[j] ,它们一定是不相等的,因为它们的长度都不同。 后缀数组:设我们用数组sa表示S的后缀数组,0<=sa[i]<=n-1,表示将S的n个后缀从小到大排序后,排名第i的后缀的位置是sa[i]。 名次数组:设我们用数组rank表示S的名次数组,0<=rank[i]<=n-1,表示将S的n个后缀从小到大排序后,后缀Suffix[i]的排名是rank[i]。很明显,sa[rank[i]]=i。 现在我们的问题是,给出一个字符串S,长度为n,S[0~n-1],字符集大小为m。求出S的后缀数组sa。有两种方法计算这个sa,倍增法和DC3法。我们设m<=n(一般情况也是这样子的吧。。)。那么倍增法的时间复杂度是O(nlog(n))

笔试算法题(40):后缀数组 & 后缀树(Suffix Array & Suffix Tree)

冷暖自知 提交于 2020-02-03 00:14:37
议题: 后缀数组(Suffix Array) 分析: 后缀树和后缀数组都是处理字符串的有效工具,前者较为常见,但后者更容易编程实现,空间耗用更少;后缀数组可用于解决最长公共子串问题,多模式匹配问题,最长回文串问题,全文搜索等问题; 后缀数组的基本元素: 给定一个string,其长度为L,后缀指的是从string的某一个位置i(0<=i<L)开始到串末尾(string[L-1])的一个子串,表示为suffix(i); L个suffix(i)按照字典顺序排列并顺序存储在一个数组SA[L]中,则SA[L]称为后缀数组,其元素值存储的是suffix(i)的起始字符在string中的位置; 每一个suffix[i]对应在SA[k]数组中的一个位置,将这个对应的位置存储为Rank[i],时间复杂度为O(N);对于任意两个 suffix[i]和suffix[j],由于知晓其在Rank[L]中的前后位置,所以在O(1)的时间内就可以得出他们的字典序大小关系; 构建SA[i]数组中相邻元素的最长公共前缀(LCP,Longest Common Prefix),Height[i]表示SA[i]和SA[i-1]的LCP(i, j);H[i]=Height[Rank[i]表示Suffix[i]和字典排序在它前一名的后缀子串的LCP大小; 对于正整数i和j而言,最长公共前缀的定义如下: LCP(i, j)

Suffix Array 后缀数组

筅森魡賤 提交于 2020-02-03 00:14:20
后缀数组 顾名思义。SuffixArray(下面有时简称SA) 和字符串的后缀有关。 后缀:字符串中某个位置一直到结尾的子串。(SA中讨论包含了原串和空串)。所以共同拥有len+1个后缀。 后缀数组: 字符串的全部后缀组成的按字典序从小到大排好的数组。因为SA中记录的都是字符串的后缀,所以SA仅仅须要记录其表示的后缀的起始位置。 因为比較字典序是O(n)的,所以暴力算法的复杂度将是O(n^2logn)。通过一些算法能够降到线性复杂度。这里先介绍一种简单的O(nlognlogn)的算法。 该算法的思想是通过倍增法减少了比較字典序的大小的复杂度O(n)到O(logn)。 其求解时不先算后缀,而是先算长度为1的子串的字典序大小排列,然后得到一个rank数组,即该子串在全部子串中排位的值。字典序越小,rank值越小。 rank[k][i] 表示起始位置为i的长度为k的子串在全部长度为k的子串中的字典序大小。 这时我们要比較长度为2k的子串的大小的话。其第i个位置的长度为2k的子串的大小能够通过比較rank[k][i]和rank[k][i+k]来实现。 SA中的sa[i]表示字典序位i的后缀串的起始位置。 const int MAXN = 100000 + 5; int _k, _len; int _rank[MAXN]; int _tmp[MAXN]; int _sa[MAXN];//