• 字符串


    字符串

    定义:

    串(string)是由零个或多个字符组成的有限序列,又名叫字符串。

    • 一般地,由n个字符串构成的串记作: S="a0a1......an-1"(n≥0),其中a_i(1≤i≤n)
      • n是一个有限的数值
      • 串一般记为S是串的名称,用双引号或单引号括起来的字符序列是串的值(引号不属于串的内容)。
      • 可以是字母、数字或其他字符,i就是该字符在串中的位置。串中的字符数目n称为串的长度,n是一个有限的数值。

    特征:

    • 结构简单,(以二进制字符为例,仅 0,1两种字符构成)
    • 规模庞大,
    • 元素(字符)重复率高

    子串

    在对字符串S做处理时,经常需要取出其中某一连续的片段,称为S的子串(substring)

    • 具体地,由串S中起始于位置i的连续k个字符组成的子串记作
      substr(S,i,k) = "aiai+1...ai+k-1",0≤i 〈 n,0≤k
    • 前缀 prefix(S,k) = substr(S,0,k);
    • 后缀 suffix(S,k) = substr(S,n-k,k)
    • 空格串:只包含空格的串。

    结论

    1. 空串是任何字符串的子串,也是任何字符串的前缀和后缀
    2. 任何字符串都是自己的子串,也是自己的前缀和后缀。此类子串,前缀和后缀分别称为平凡子串,平凡前缀,平凡后缀
    3. 字符串本身之外的所以非空子串,前缀,后缀,分别称为真子串,真前缀,真后缀

    判定

    一对字符串S="a0a1......an-1" 和 T="b0b1......bm-1",当且仅当二者长度相等(n=m),
    且对应的字符分别相等(对任何0≤i 〈n,都有ai = bi)

    模式匹配算法

    朴素的模式匹配算法(BF(Brute-Force)算法)

    • 基本思想是:
      1. 从主串的第一个字符起与子串的第一个字符进行比较,若相等,则继续逐对字符进行后续的比较;
      2. 若不相等,则从主串第二个字符起与子串的第一个字符重新比较,以此类推,
        直到子串中每个字符依次和主串中的一个连续的字符序列相等为止,此时称为匹配成功。
      3. 如果不能在主串中找到与子串相同的字符序列,则匹配失败。
    • BF算法是最原始、最暴力的求解过程,但也是其他匹配算法的基础

    public static void bruteForce(String s, String p) {
            int index = -1;// 成功匹配的位置
            int sLength = s.length();// 主串长度
            int pLength = p.length();// 子串长度
            if (sLength < pLength) {
                System.out.println("Error.The main string is greater than the sub string length.");
                return;
            }
            int i = 0;
            int j = 0;
            while (i < sLength && j < pLength) {
                if (s.charAt(i) == p.charAt(j)) {// 判断对应位置的字符是否相等
                    i++;// 若相等,主串、子串继续依次比较
                    j++;
                } else {// 若不相等
                    i = i - j + 1;// 主串回溯到上次开始匹配的下一个字符
                    j = 0;// 子串从头开始重新匹配
                }
            }
            if (j >= pLength) {// 匹配成功
                index = i - j;
                System.out.println("Successful match,index is:" + index);
            } else {// 匹配失败
                System.out.println("Match failed.");
            }
        }
    
    

    KMP

    P模式匹配算法,是一个效率非常高的字符串匹配算法。其全称是Knuth–Morris–Pratt string searching algorithm

    • 其核心思想就是主串不回溯,模式串尽量多地往右移动
    • 即: N(P, j) = {t | prefix(prefix(P, j), t) = suffix(prefix(P, j), t), 0≤ t 〈 j } shift = j -t shift就是本次移动
      地最大步长

    构建next表

    1. 因为空串是任何非空串的真字串,真前缀,真后缀,故只要 j > 0,则必有 0 ∈ N(P, j)
      此时N(P, j) 必非空,从而保证“在其中取最大值”这一操作可行。反之。若j=0,则前缀prefix(P, j)
      本身就是空串,它没有真子串,于是必有集合N(P, j) = φ。此种情况下,next[0] 该如何定义呢?
    2. 按照串匹配算法的构思,倘若某轮迭代中第一对字符即失配,则应该将模式串P直接右移一位,然后从其首字符继续下一轮对比
      就实际效果而言,这一处理方法完全等价于“令next[0] = -1”。
    3. 下面以模式串 "ABCDABD" 为例来详细说明是如何构建next表
    P = ABCDABD
    j = 0, prefix(P, 0) = φ
    next[0] = -1;//规定如此
    
    P = ABCDABD
    j = 1, prefix(P, 1) = A
    真前缀: φ
    真后缀: φ
    next[1] = 0;
    
    P = ABCDABD
    j = 2, prefix(P, 2) = AB
    真前缀: A
    真后缀: B
    next[2] = 0;
    
    P = ABCDABD
    j = 3, prefix(P, 3) = ABC
    真前缀: A,AB
    真后缀: BC,C
    next[3] = 0;
    
    P = ABCDABD
    j = 4, prefix(P, 4) = ABCD
    真前缀: A,AB,ABC
    真后缀: BCD,CD,D
    next[4] = 0;
    
    P = ABCDABD
    j = 5, prefix(P, 5) = ABCDA
    真前缀: A,AB,ABC,ABCD
    真后缀: BCDA,CDA,DA,A
    next[5] = 1;
    
    P = ABCDABD
    j = 6, prefix(P, 6) = ABCDAB
    真前缀: A,AB,ABC,ABCD,ABCDA
    真后缀: BCDAB,CDAB,DAB,AB,B
    next[6] = 2;
    
    得出next表为:
    [-1, 0, 0, 0, 0, 1, 2]
    
    

    接下来就是代码实现:

    //第一步构造next表
    public static int[] buildNext(String p){
            //构建next表就是查找真前缀 == 真后缀的最大长度,以获取模式串尽量多地往右移动
            int[] N = new int[p.length()];
            int m = p.length(),j = 0;//主串位置
            int t = N[0] = -1;//字串位置
            while(j < m -1){
                if(t < 0 || p.charAt(j) == p.charAt(t)){
                    j++;t++;
                    N[j] = t;
                }else{//失配
                    t = N[t];
                }
            }
            return N;
        }
    //第二步利用next表尽量多地往右移动
    public static void kmp(String s, String p) {
            int[] next = buildNext(p);// 调用next(String p)方法
            int index = -1;// 成功匹配的位置
            int sLength = s.length();// 主串长度
            int pLength = p.length();// 子串长度
            if (sLength < pLength) {
                System.out.println("Error.The main string is greater than the sub string length.");
                return;
            }
            int i = 0;
            int j = 0;
            while (i < sLength && j < pLength) {
                /*
                 * 如果j = -1, 或者当前字符匹配成功(即s.charAt(i) == p.charAt(j)), 都令i++,j++
                 * 这两个条件能否交换次序?
                 */
                if (j == -1 || s.charAt(i) == p.charAt(j)) {
                    i++;
                    j++;
                } else {
                    /*
                     * 如果j != -1,且当前字符匹配失败, 则令 i 不变,j = next[j], next[j]即为j所对应的next值
                     */
                    j = next[j];
                }
            }
            if (j >= pLength) {// 匹配成功
                index = i - j;
                System.out.println("Successful match,index is:" + index);
            } else {// 匹配失败
                System.out.println("Match failed.");
            }
        }
    
  • 相关阅读:
    结对四则运算04—网页版
    每周总结07
    每周总结06
    求循环数组中最大子数组的和
    每周总结05
    每周总结03
    每周总结04
    四则运算03
    开发过程记录
    Ubuntu下安装mysql和可视化工具超级简单教程
  • 原文地址:https://www.cnblogs.com/AronJudge/p/14527066.html
Copyright © 2020-2023  润新知