字符串匹配算法

字符串匹配算法

暴力匹配算法

每次都从父亲的当前位置i开始匹配,最坏情况下每一个i都要匹配,每次匹配子串的长度次,比如父串aaaaaab,子串b,时间复杂度为O(n^2)

//返回如果是子串的话,第一个起点的位置
public static int getIndexOf1(String father, String son) {
    if (father==null||son==null||father.length() < son.length())
        return -1;
    int len1 = father.length();
    int len2 = son.length();
    char[] fa = father.toCharArray();
    char[] so = son.toCharArray();
    for (int i = 0; i < len1 - len2; i++) {
        boolean isSame = true;
        for (int j = 0; j < len2; j++) {
            if (fa[i + j] != so[j]) {
                isSame = false;
                break;
            }
        }
        if (isSame)
            return i;
    }
    return -1;
}

KMP算法

前缀知识:最长前缀和最长后缀匹配

举个例子:比如给定字符串aabaabc。

对于第一个字符a来说,无前缀和后缀,所以它的next数组是-1,对于第二个字符a来说,前缀和后缀都是a,但是此时前缀和后缀都是前面串的全部了,不允许,所以它的next数组是0。为什么第一个没有是-1,第二个没有填0?第一个是前后缀都没有,没资格,所以-1;第二个是有,但是前后缀重合,不允许,所以下一个匹配的字符从0开始。

对于后面的字符来说,每次都是去它的前缀和后缀中找最长的,以最后一个字符c为例,它的最长前缀和最长后缀匹配的应该是aab,所以它的next数组处填3,其余以此类推。

得到next数组:
public static int[] getNextArray(char[] str) {
    if (str.length == 1)
        return new int[]{-1};
    int[] next = new int[str.length];
    next[0] = -1;
    next[1] = 0;
    int i = 2;
    int cn = 0;
    while (i < next.length) {
        if (str[i - 1] == str[cn])
            next[i++] = ++cn;
        else if (cn > 0)
            cn = next[cn];
        else
            next[i++] = 0;
    }
    return next;
}

解释一下代码逻辑:如果字符串长度为1,没有公共前后缀,直接返回-1即可。

根据前面的逻辑,直接填好next数组的前两个位置,然后i表示从当前位置开始,cn表示在i之前的字符串中,公共前后缀中前缀的下一个字符位置。

如果i-1处字符等于cn处的字符,也就是说当前失效匹配位置前的字符,等于上一个最长前缀的后一个字符,那么当前位置的最长前后缀长度应该是上一个的最长前缀的字符+1,如果不相等,那么就应该在上一个前缀中,循环该寻找过程,直到它回到0的起始位置,前面没东西了,那么就让此时的next数组处变为0。

KMP算法主函数位置:

如果两个字符串当前位置都匹配成功,x和y同时后移。否则,当前位置匹配失效,如果next[y]=-1说明第一个字符就失败了,那还匹配个集贸。直接让x后移一位,换人来;否则,让y去它的最长前缀位置,此时x不动,然后两个再去匹配,循环往复。

如果最后y到了子串的末尾的下一个位置,那么第一个位置就应该是x-y处,比如abbca,bbc,那么x=4,y=3,第一个位置就应该是x-y。

如果没有到达该位置,都跑完了,你还到达不了,返回-1。

//todo:两个信息,为什么从j位置出发,之前的位置为什么不用考虑;第二个问题:为什么从当前字符匹配,前面的0-j字符不用匹配了嘛
public static int getIndexOf2(String s1, String s2) {
    if (s1 == null || s2 == null || s1.length() < s2.length() || s2.length() < 1)
        return -1;
    char[] str1 = s1.toCharArray();
    char[] str2 = s2.toCharArray();
    int[] next = getNextArray(str2);
    int x = 0, y = 0;
    while (x < str1.length && y < str2.length) {
        if (str1[x] == str2[y]) {
            ++x;
            ++y;
        } else if (next[y] == -1)
            ++x;
        else
            y = next[y];
    }
    return y == str2.length ? x - y : -1;
}

假设当前子串失效位置为y,为什么你可以直接回到前面的最长公共前缀位置处?为什么不用重新匹配?

理由:计算next数组时,计算的是最长前缀和后缀的匹配,所以必然相等。

假设当前父串失效位置为x,为什么可以不用回退?

理由:next数组已经给你计算过了,如果你回退,说明你前面的next数组计算错误,你活该死去。

对数器校验:

// 生成指定长度的随机字符串
public static String generateRandomString(int length) {
    String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    Random random = new Random();
    StringBuilder sb = new StringBuilder(length);
    for (int i = 0; i < length; i++) {
        int randomIndex = random.nextInt(characters.length());
        char randomChar = characters.charAt(randomIndex);
        sb.append(randomChar);
    }
    return sb.toString();
}
​
public static void main(String[] args) {
    int times = 1000000;
    //跑times次,错一次就提前终止,并输出此时的字符串
    for (int i = 0; i < times; i++) {
        Random random = new Random();
        int n = random.nextInt(20) + 10;
        int m = random.nextInt(10) + 10;
        //保证n一定不比m小
        if (n < m) {
            n ^= m;
            m ^= n;
            n ^= m;
        }
        String str = generateRandomString(n);
        String str2 = generateRandomString(m);
        if(getIndexOf1(str,str2)!=getIndexOf2(str,str2))
        {
            System.out.println("程序出错,检查算法");
            System.out.println("第一个字符串"+str);
            System.out.println("第二个字符串"+str2);
            return ;
        }
    }
    System.out.println("程序结束");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值