串 && KMP

来源:互联网 发布:visio数据库模型图 编辑:程序博客网 时间:2024/06/06 05:16

串的定义

串是由零个或多个字符组成的有限序列,又名叫字符串。一般记为s="a1a2a3...an"n>=0
串中的字符数目n称为串的长度。

n==0的串也就是零个字符的串称为空串,长度为零,使用""表示。

空格串,是指只包含空格的串,空格串是有长度的。

子串个数

串中任意字符的长度的子序列称为子串,子串在主串中的位置就是子串的第一个字符在主串中的序号。

长度为n的串的非空子串个数F(n)

F(n)=n(n+1)2

考虑空子串的话要加一

串的操作

和线性表不同,串更关注的是查找子串的位置,得到指定位置的子串和替换子串等。

朴素的模式匹配

步骤

简单地说,就是对主串的每一个字符作为子串开头,与要匹配的字符串进行匹配。对主串做大循环,每个字符开头做T的长度的小循环,知道匹配成功或者全部遍历为止。

代码

package com.weixuan.test;public class Bunch {    private static final Integer FALSE = -1;    /**     * @Description: 返回子串target在主串original中第i个字符之后     * @param @param     *            original     * @param @param     *            target     * @param @return     * @return int     */    public int getIndex(String original, String target) {        if (original == null || original.length() <= 0)            return Bunch.FALSE;        if (target == null || target.length() <= 0)            return Bunch.FALSE;        int j = 0, i = 0;        while (i < original.length() && j < target.length()) {            if (original.charAt(i) == target.charAt(j)) {                i++;                j++;            } else {                /**                 * 后退到上次匹配的位置的下一位                 */                i = i - j + 1;                j = 0;            }        }        if (j >= target.length())            return i - target.length();        return Bunch.FALSE;    }    public static void main(String[] args) {        System.out.println(new Bunch().getIndex("abcdefg", "dbc"));    }}

性能

主串长度 :M,子串长度:N

最好的情况:O(1)

平均情况:O(M+N),平均是(m+n)/2次找

最差情况,每次不成功的匹配都是发生在子串的最后一个字符,O((MN+1)N)

KMP

next数组

S是主串,T是子串。j值的变换与主串没什么关系,j值取决于当前字符之前的串的前后缀的相似程度。

next[i][j]=0,max(k|1<k<j,p1...pk1=pjk+1...pj1),1, j = 1

几个例子:

  1. T = “abcdex”

j=1,next[1]=0

j=2,j 由 1到 j1 的字符只有a,属于其他情况 next[2]=1

j=3,j,由 1到 j1 的字符只有ab,显然ab不相等,属于其他情况 next[3]=1

j=4,j由 1到 j1 的字符只有abc,显然abc不相等,属于其他情况 next[4]=1

以后同理,所以T串的next[j]如下

j 1 2 3 4 5 6 next[j] 0 1 1 1 1 1

2. T = “abcdex”

j=1,next[1]=0

j=2,j 由 1到 j1 的字符只有a,属于其他情况 next[2]=1

j=3,j 由 1到 j1 的字符只有ab,显然ab不相等,属于其他情况 next[3]=1

j=4,j 由 1到 j1 的字符只有abc,显然abc不相等,属于其他情况 next[4]=1

j=5,j 由 1到 j1 的字符为abca,前缀字符a与后缀字符相等,p1==p4,由公式p1...pk1=pjk+1...pj1 可得,jk+1=4j=5==>k=2,即k = 2.所以next[5]=2

j=6,j由 1到 j1 的字符为abcab,前缀字符ab与后缀字符ab相等,p1p2==p4p5,由公式p1...pk1=pjk+1...pj1 可得,jk+1=4j=6==>k=3,即k = 3.所以next[6]=3

所以T串的next[j]如下

j 1 2 3 4 5 6 next[j] 0 1 1 1 2 3

3. T = “ababaaaba”

j=1,next[1]=0

j=2,j 由 1到 j1 的字符只有a,属于其他情况 next[2]=1

j=3,j 由 1到 j1 的字符只有ab,显然ab不相等,属于其他情况 next[3]=1

j=4,j 由 1到 j1 的字符只有aba,前缀与后缀相等,根据公式,jk+1=3j=4k=2 next[4]=2

j=5,j 由 1到 j1 的字符为abab,前缀字符ab与后缀字符ab相等,p1p2==p3p4,由公式p1...pk1=pjk+1...pj1 可得,jk+1=3j=5==>k=3,即k = 3.所以next[5]=3

j=6,j由 1到 j1 的字符为ababa,前缀字符aba与后缀字符aba相等,p1p2p3==p3p4p5,由公式p1...pk1=pjk+1...pj1 可得,k1=3k=4,即k = 4.所以next[6]=4

j=7,j由 1到 j1 的字符为ababaa,前缀字符ab与后缀字符aa不相等,只有a相等,p1==p6,由公式p1...pk1=pjk+1...pj1 可得,k1=1k=2,即k = 2,所以next[7]=2

j=8,j由 1到 j1 的字符为ababaaa,前缀字符ab与后缀字符aa不相等,只有a相等,p1==p7,由公式p1...pk1=pjk+1...pj1 可得,k1=1k=2,即k = 2,所以next[8]=2

j=9,j由 1到 j1 的字符为ababaaab,前缀字符ab与后缀字符ab相等,p1p2==p7p8,由公式p1...pk1=pjk+1...pj1 可得,k1=2k=3,即k = 3,所以next[9]=3

所以T串的next[j]如下

j 1 2 3 4 5 6 7 8 9 next[j] 0 1 1 1 2 3 2 2 3
  1. T = “aaaaaaaab”

j=1,next[1]=0

j=2,j 由 1到 j1 的字符只有a,属于其他情况 next[2]=1

j=3,j 由 1到 j1 的字符只有aa,显然aa相等,p1==p2,k1=1,k=2 next[3]=2

j=4,j 由 1到 j1 的字符只有aaa,显然前缀aa与后缀aa相等,p1p2==p2p3,k1=2,k=3 next[4]=3

….
j=9,j 由 1到 j1 的字符是aaaaaaaa,显然前缀aaaaaaa与后缀aaaaaaa相等,p1p2p3p4p5p6p7==p2p3p4p5p6p7p8,k1=7,k=8 next[9]=8

j 1 2 3 4 5 6 7 8 9 next[j] 0 1 2 3 4 5 6 7 8

kmp算法

public int KMP(String original, String target,int pos){    int i = pos, j = 1;    int pLen = target.length();    int mLen = original.length();    char[] mChar = original.toCharArray();    /**        * 模式字符串字符数组从第二个元素位置开始存放    */    char[] pChar = new char[pLen + 1];    System.arraycopy(target.toCharArray(), 0, pChar, 1, pLen);    int next[] = getNextVal(target);    while (i < mLen && j <= pLen) {        if (j == 0 || mChar[i] == pChar[j]) {            i++;            j++;        } else {            j = next[j];// 匹配失败,回溯        }    }    /**        * 主字符串中存在模式模式字符串    */    if (j > pLen) {        /**            * 返回模式字符串在主字符串中出现的位置        */        return i - pLen;    }    return Bunch.FALSE;}

性能

O(M+N)

KMP算法的改进

nextval

public static int[] getNextVal(String target) {        int i = 1, j = 0, len = target.length();        char[] pChar = new char[len + 1];        System.arraycopy(target.toCharArray(), 0, pChar, 1, len);        int[] next = new int[len + 1];        while (i < len) {            if (j == 0 || pChar[i] == pChar[j]) {                i++;                j++;                if (pChar[i] != pChar[j]) {                    next[i] = j; // 设置回溯值                } else {                    next[i] = next[j];// 如果两个字符相等,那么将pChar[j]的回溯值复制给pChar[i]                }            } else {                j = next[j];// 从该位置回溯            }        }        return next;    }

nextval的具体过程

T = “ababaaaba”

j=1,next[1]=0,nextVal[1]=0

j=2,next[2]=1,第一位是a,不相等。所以nextVal[2]=1,维持原值。

j=3,next[3]=1,与第一位相等,所以nextVal[3]=nextVal[1]=0

j=4,next[4] = 2,"b"nextVal[4]= nextVal[2] = 1$

j=5,next[5]=3,第五个字符与第三个字符a相等,nextVal[5]=nextVal[3]=0

j=6,next[6]=4,第六个字符与第4个字符b不相等,nextVal[6]=4

j=7,next[7]=2,第七个字符与第2个字符b不相等,nextVal[7]=2

j=8,next[8]=2,第八个字符与第2个字符b相等,nextVal[8]=nextVal[2]=1

j=9,next[9]=3,第九个字符与第3个字符a相等,nextVal[9]=nextVal[3]=0

所以T串的next[j]如下

j 1 2 3 4 5 6 7 8 9 next[j] 0 1 1 1 2 3 2 2 3 nextVal[j] 0 1 0 1 0 4 2 1 0
0 0
原创粉丝点击