数据结构--串
来源:互联网 发布:淘宝卖家开花呗条件 编辑:程序博客网 时间:2024/06/07 17:58
1、基本概念
串是由零个或多个字符组成的有限序列,又名字符串。
1.1 串大小的比较
给定两个串
s=”
t=”
当满足以下条件之一,s<t
(1)n<m 且
(2)存在某个值k<=min(m,n) 使得
2、串的匹配
主串S=”goodgoogle” , 子串T=”google”
2.1 朴素的匹配匹配算法
子串匹配主串,由子串的第一个字符与主串的第一个字符开始比较,若相同,比较下一个。
如果完全相同,匹配成功。
如果有一个不同,用子串的第一个字符与主串的第二个字符比较。如此循环。时间复杂度为O(m*n)
public static int match(String s, String t){ if(s==null || t==null || s.length()<=0 || t.length()<=0 || s.length()<t.length()){ System.out.println("输入参数不正确"); return -1; } int i=0,j=0; while(i<s.length() && j<t.length()){ if(s.charAt(i) == t.charAt(j)){ //如果相同,比较下一个 i++; j++; } else { //如果有一个不同,i回溯到匹配前加1的位置 i = i-j+1; j=0; } } if(j == t.length()){//如果匹配的子串长度与子串相同,则完成匹配 return i-j; } return -1; }
2.2 KMP算法
朴素匹配算法会一直回溯主串,导致很多不必要的比较。KMP算法就是保证主串不回溯,从而提高效率。
预处理时间复杂度为O(m),匹配时间复杂度为O(n)
2.2.1 KMP算法的简单理解
主串s=”BBC ABCDAB ABCDABCDABDE”
子串t=”ABCDABD”
2.2.2 子串的预处理
首先需要知道前缀和后缀的概念
前缀:除了最后一个字符以外,一个字符串的全部头部组合;
后缀:除了第一个字符以外,一个字符串的全部尾部组合。
预处理就是获取前缀和后缀的最长的共有元素的长度
子串处理的步骤
“A”的前缀和后缀都为空集,共有元素的长度为0;
“AB”的前缀为[A],后缀为[B],共有元素的长度为0;
“ABCD”的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;
“ABCDA”的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为”A”,长度为1;
“ABCDABD”的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。
如果将上述的元素组成数组 [0, 0, 0, 0, 1, 2, 0],成为next数组
Max即是共有元素的最大长度
2.2.3 匹配步骤
a、开始匹配
子串的第一个与主串比较,如果不同则使用主串的后一个与子串比较
b、匹配到部分相同
如图,主串的第4个元素与子串匹配相同,直到第11个不同。现在相同的部分为ABCDAB。通过上面对子串的预处理可以知道,子串有公共的元素为AB,长度为2
现在已知的条件:
主串与子串相同的部分为ABCDAB
子串有相同的部分AB
所以再次比较时 子串的AB不再与主串的ABCDAB比较 直接使用子串第一个AB后的字符C与主串第二个AB后的字符 空字符 比较。因为AB是已知相同的。这就是KMP算法的核心原理了
c、失配时再次比较
上一次子串的字符C和主串的空字符不匹配,则使用子串的第一个字符进行比较。如此循环,知道完成
步骤b 匹配到部分相同,子串C与主串空字符比较时,跳过的步骤为匹配元素的长度6 - 相同部分AB的长度2 = 4
public class Main { public static int[] getNext(String sub) { int[] next = new int[sub.length()]; next[0] = -1; int k = -1; int j = 0; while (j < sub.length()-1) { //k表示前缀,j表示后缀 if (k == -1 || sub.charAt(j)==sub.charAt(k)) { ++k; ++j; next[j] = k; } else { k = next[k]; } } return next; } public static int indexOf(String src, String ptn) { int i = 0, j = 0; int sLen = src.length(); int pLen = ptn.length(); int[] next = getNext(ptn); System.out.println(Arrays.toString(next)); while (i < sLen && j < pLen) { // 如果j = -1,或者当前字符匹配成功(src.charAt(i)==ptn.charAt(j)),都让i++,j++ if (j==-1 || src.charAt(i)==ptn.charAt(j)) { i++; j++; } else { // 如果j!=-1且当前字符匹配失败,则令i不变,j=next[j],即让pattern模式串右移j-next[j]个单位 j = next[j]; } } if (j == pLen) return i - j; return -1; } public static void main(String[] args) { System.out.println(""+indexOf("BBC ABCDAB ABCDABCDABDE","ABCDABD")); }}
2.2.4 Next 数组的优化
public static int[] getNext(String sub) { int[] next = new int[sub.length()]; next[0] = -1; int k = -1; int j = 0; while (j < sub.length()-1) { //k表示前缀,j表示后缀 if (k == -1 || sub.charAt(j)==sub.charAt(k)) { ++k; ++j; if(sub.charAt(j) != sub.charAt(k)) next[j] = k; else//如果有与以前字符相同的字符,则使用以前字符的标记 next[j] = next[k]; } else { k = next[k]; } } return next; }
参考:
next数组的解释:http://www.cnblogs.com/fuck1/p/6059736.html
长篇大论:http://blog.csdn.net/v_july_v/article/details/7041827#
代码:http://www.jianshu.com/p/e2bd1ee482c3
- 数据结构,串
- 数据结构---串
- 数据结构--串
- 数据结构-串
- 数据结构 串
- 数据结构 串
- 【数据结构】串
- 数据结构--串
- 【数据结构】-串
- 数据结构----串
- 数据结构-串
- 数据结构:串
- [数据结构]串
- [数据结构]串
- 数据结构--串
- 数据结构---串
- 数据结构----串
- 数据结构:串
- oss endpoint
- takephoto图片选择器
- swing入门Helloword
- hpuoj 【1144】n的合成 【数学】&&【思维】
- TypeError:__init__() got an unexpected keyword argument 'xxx'
- 数据结构--串
- 输入两个链表,找出它们的第一个公共结点。
- bootstrap日历控件
- acm Subsequence 3061
- Gradle从入门到实战
- c++学习系列之vector
- MFC之路 串口通信篇(之二)
- 学生信息系统优化总结
- Python学习(二)——控制流