标签: 字符串匹配

LeetCode刷题【1392】最长快乐前缀

「快乐前缀」 是在原字符串中既是 非空 前缀也是后缀(不包括原字符串自身)的字符串。

给你一个字符串 s,请你返回它的 最长快乐前缀。如果不存在满足题意的前缀,则返回一个空字符串 "" 。

 

示例 1:

输入:s = "level"
输出:"l"
解释:不包括 s 自己,一共有 4 个前缀("l", "le", "lev", "leve")和 4 个后缀("l", "el", "vel", "evel")。最长的既是前缀也是后缀的字符串是 "l" 。

示例 2:

输入:s = "ababab"
输出:"abab"
解释:"abab" 是最长的既是前缀也是后缀的字符串。题目允许前后缀在原字符串中重叠。

 

提示:

  • 1 <= s.length <= 105
  • s 只含有小写英文字母
Related Topics
  • 字符串
  • 字符串匹配
  • 哈希函数
  • 滚动哈希

  • 👍 94
  • 👎 0
  • KMP模式串预处理方法
    其实就是KMP算法中模式串(Pattern)的预处理算法

    整个过程也可以当做是一个动态规划的算法过程来理解,其本质是状态机,额,这块就涉及知识盲区了,大学的知识早就忘记了。

    还是回到动态规划的话题来说吧,比较好理解点

    借用一个案例模式串来讲解下

    A B A C D A B A B C

    我们需要定义的DP数组的含义是,从下标0开始到当前字符结束的子串中,最长的前缀和结尾相同的子串的结束下标,感觉非常绕不像人话。

    以上面的子串A B A C D A B A为例,我们解释一下,这部分内容的 前缀和结尾相同的最长部分为A B A

    他在头部的结束下标为 2 ,对应为第二个A 字符所以对应的这部分为dp[7] = 2,或者简单理解为该子串的长度-1

    对于前面没有匹配的部分,我们用-1来表示,这样我们可以直接先目测得到上面案例字符串算出来的dp[]数组为

     A   B   A   C   D   A   B   A   B   C
    -1  -1   0  -1  -1   0   1   2   1  -1

    根据上面的结果我们可以看到,dp[i]的值依赖于 pattern[i]pattern[dp[i-1]+1]的匹配情况

    如果能匹配则dp[i] = dp[i-1] + 1

    不能匹配的话则继续与pattern[dp[dp[i-1]]+1]匹配,不能的话则继续再往前查找

    照旧拿上面的案例举个栗子

                         A   B   A   C   D   A   B   A   B
                        -1  -1   0  -1
    
     A   B   A   C   D   A   B   A   B
                         0   1   2   ↑

    箭头部分的B与前缀的C未能匹配,所以继续前移,找到前面的字符Adp[7] = 2的值作为下标的时候dp[2] = 0的情况下,如下

                                 A   B   A   C   D   A   B   A   B
                                -1  -1   0  -1
    
     A   B   A   C   D   A   B   A   B
                         0   1   2   ↑

    发现能匹配完成,则最终得到对应的dp[8] = dp[2] + 1 = 1

    最终我们可以知道数组末尾的值就是题目要求的最长快乐前缀的结束字符下标,在KMP中一般用next[]数组表示,而不是写成dp[]数组

    代码

    class Solution {
        public String longestPrefix(String s) {
            int[] next = getNextArr(s.toCharArray());
            int last = next[next.length - 1];
            if (last == -1) return "";
            return s.substring(0, last + 1);
        }
        private int[] getNextArr(char[] arr) {
            int[] next = new int[arr.length];
            Arrays.fill(next, -1);
            int idx = 0;
            int p = -1;
            while (++idx < arr.length){
                while ( p != -1 && arr[p+1] != arr[idx]){
                    p = next[p];
                }
                if (arr[p+1] == arr[idx]){
                    p++;
                }
                next[idx] = p;
            }
            return next;
        }
    }

    LeetCode刷题【面试题 17.17】多次搜索

    给定一个较长字符串big和一个包含较短字符串的数组smalls,设计一个方法,根据smalls中的每一个较短字符串,对big进行搜索。输出smalls中的字符串在big里出现的所有位置positions,其中positions[i]smalls[i]出现的所有位置。

    示例:

    输入:
    big = "mississippi"
    smalls = ["is","ppi","hi","sis","i","ssippi"]
    输出: [[1,4],[8],[],[3],[1,4,7,10],[5]]
    

    提示:

    • 0 <= len(big) <= 1000
    • 0 <= len(smalls[i]) <= 1000
    • smalls的总字符数不会超过 100000。
    • 你可以认为smalls中没有重复字符串。
    • 所有出现的字符均为英文小写字母。
    Related Topics
  • 字典树
  • 数组
  • 哈希表
  • 字符串
  • 字符串匹配
  • 滑动窗口

  • 👍 41
  • 👎 0
  • smalls数组构建字典树

    1. smalls数组构建字典树
    2. big数组从第i位开始到结尾的子序列拿到字典树上匹配,i从0开始分别求解,直到结束
    3. 因为匹配的时候需要知道当前字典树上字符串结束节点是哪个字符的,所以字典树的节点需要额外记录下各自归属与smalls数组中对应字符串的下标
    4. 在树上匹配的过程中,但凡匹配到了一个结束字符,就在结果数组的对应这个字符串下标的子数组中插入一个当前匹配的big字符串子序列的开始下标

    代码

    class Solution {
        public int[][] multiSearch(String big, String[] smalls) {
            Trie trie = new Trie();
            trie.smallsCount = smalls.length;
            trie.insert(smalls);
            return trie.search(big);
        }
    
    
        public class Trie{
    
            Node tree;
            int smallsCount = 0;
    
            public Trie(){
                tree = new Node();
            }
    
            void insert(String[] strArr){
                for (int idx = 0; idx < strArr.length; idx++) {
                    Node cur = tree;
                    String str = strArr[idx];
                    int i = -1;
                    while (++i < str.length()){
                        int child = str.charAt(i)-'a';
                        if (cur.children[child] == null){
                            cur.children[child] = new Node();
                        }
                        cur = cur.children[child];
                    }
                    cur.flag = true;
                    cur.smallIndex = idx;
                }
            }
    
            int[][] search(String str){
                int[][] res = new int[smallsCount][];
                List<List<Integer>> list = new ArrayList<>();
                for (int i = 0; i < smallsCount; i++) {
                    list.add(new ArrayList<>());
                }
    
                for (int i = 0; i < str.length(); i++) {
                    String subStr = str.substring(i);
                    Node cur = tree;
                    int idx = -1;
                    while (cur != null && ++idx < subStr.length()){
                        char c = subStr.charAt(idx);
                        if (cur.children[c-'a'] != null){
                            cur = cur.children[c-'a'];
                            if (cur.flag){
                                list.get(cur.smallIndex).add(i);
                            }
                        }else{
                            break;
                        }
                    }
                }
                for (int i = 0; i < list.size(); i++) {
                    res[i] = list.get(i).stream().mapToInt(Integer::intValue).toArray();
                }
                return res;
            }
    
    
    
    
            class Node{
                boolean flag = false;
                Node[] children;
                int smallIndex = -1;
                public Node(){
                    children = new Node[26];
                }
            }
        }
    }

    LeetCode刷题【28】实现 strStr()

    实现 strStr() 函数。

    给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回  -1

    说明:

    当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

    对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

     

    示例 1:

    输入:haystack = "hello", needle = "ll"
    输出:2
    

    示例 2:

    输入:haystack = "aaaaa", needle = "bba"
    输出:-1
    

     

    提示:

    • 1 <= haystack.length, needle.length <= 104
    • haystackneedle 仅由小写英文字符组成
    Related Topics
  • 双指针
  • 字符串
  • 字符串匹配

  • 👍 1471
  • 👎 0
  • Sunday算法实现

    举个栗子说明,haystack字符串

    h, e, l, l, o, s, h, e, l, l, d, o, h, e, l, d, l, o, h, e, l, w, l, o, q, h, e, l, l, o, w, h, e, l, l, o
    0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

    needle字符串

    l, o, h, e, l, w, l
    0  1  2  3  4  5  6
    1. 从头开始匹配,匹配失败,
    2. 找到haystack第7个字符下标6位置的字符hh字符在needle字符串l, o, h, e, l, w, l最后出现的下标为2,则从下标6位置的字符h往前找2位开始匹配,
    3. 此时新开始匹配的下标为4,字符为o,依旧匹配失败,继续往后找needle字符串长度的位置在下标11位置发现字符o,此时oneedle中最后出现的位置为下标1,则往前找一位,从haystack的10下标开始匹配
    4. 10下标的d不能和needle匹配,继续往后找needle长度个位置,依旧是字符o,在needle中最后出现的位置为下标1,则往前找一位从haystack的第16下标开始匹配,
    5. 能完全匹配,结束,返回当前开始的下标16。

    代码

    class Solution {
        //Sunday算法实现
        public int strStr(String haystack, String needle) {
            int n = haystack.length();
            int m = needle.length();
            int[] lastPos = new int[256];
            for (int i = 0; i < needle.length(); i++) {
                lastPos[needle.charAt(i)] = i;
            }
            for (int i = 0; i + m <= n; i += (m - lastPos[haystack.charAt(i+m)])) {
                boolean flag = true;
                for (int j = 0; j < m; j++) {
                    if (haystack.charAt(i+j) == needle.charAt(j)){
                        continue;
                    }
                    flag = false;
                    break;
                }
                if (flag){
                    return i;
                }
                if (i + m >= n) break;
            }
            return -1;
        }
    }