KMP算法时间复杂度的推导

//时间复杂度O(n) n为s1的长度
    public static int getIndexOf(String s1, String s2) {
        if (s1 == null || s2 == null || s2.length() < 1 || s1.length() < s2.length()) {
            return -1;
        }
        char[] str1 = s1.toCharArray();
        char[] str2 = s2.toCharArray();
        int x = 0;//str1上的配对位置
        int y = 0;//str2上的配对位置
        //O(m) m<=n
        int[] next = getNextArray(str2);
        //O(n)
        while (x < str1.length && y < str2.length) {
            if (str1[x] == str2[y]) {
                x++;
                y++;
            } else if (next[y] == -1) {//说明开始位置就对不上x应该往下走
                x++;
            } else {
                y = next[y];
            }
        }
        return y == str2.length ? x - y : -1;
    }

    public static int[] getNextArray(char[] str2) {
        if (str2.length == 1) {
            return new int[]{-1};
        }
        int[] next = new int[str2.length];
        next[0] = -1;
        next[1] = 0;
        int i = 2;//表示哪个位置在求next数组值
        int cn = 0;//表示当前是哪个位置的值在和i-1位置的字符比较
        while (i < str2.length) {
            if (str2[i - 1] == str2[cn]) {
                next[i++] = ++cn;//这里意思是i位置的值为cn+1同时cn也会加一成为新的cn
            } else if (cn > 0) {
                cn = next[cn];//没配对上继续找前缀位置来配
            } else {
                next[i++] = 0;//cn为0且配对不上 所以为0
            }
        }
        return next;
    }

    // for test
    public static String getRandomString(int possibilities, int size) {
        char[] ans = new char[(int) (Math.random() * size) + 1];
        for (int i = 0; i < ans.length; i++) {
            ans[i] = (char) ((int) (Math.random() * possibilities) + 'a');
        }
        return String.valueOf(ans);
    }

    public static void main(String[] args) {
        int possibilities = 5;
        int strSize = 20;
        int matchSize = 5;
        int testTimes = 5000000;
        System.out.println("test begin");
        for (int i = 0; i < testTimes; i++) {
            String str = getRandomString(possibilities, strSize);
            String match = getRandomString(possibilities, matchSize);
            if (getIndexOf(str, match) != str.indexOf(match)) {
                System.out.println("Oops!");
            }
        }
        System.out.println("test finish");
    }

根据代码我们来分析时间复杂度为什么是O(n) n是s1的长度

1.首先分析求next数组的复杂度

方法里面涉及到了两个变量i和cn,i没有回退,cn却会回退,所以没法估计i和cn的变化次数进而无法知道复杂度

这里我们引用新的变量i-cn,此时我们会发现此变量在while循环里面也是不会回退的,他的最大值为m(s2的长度)i的最大值也是m

下面分析if条件中的i和i-cn的变化

条件 i i-cn
第一个分支
+ 不变
第二个分支
不变 +
第三个分支
+ +

 

根据上面表格分析可知,每一次while循环无论进哪个分支条件,都是引起i或者i-cn值的变大,两者最大值都是m,所以最多有2m次的循环,使他们都到达峰值

所以求next数组的整体时间复杂度就是O(m)

2.同理我们分析接下来的while循环的复杂度 x和x-y的最大值都是n(s1的长度)

下面分析if条件中的x和x-y的变化

条件 x x-y
第一个分支
+ 不变
第二个分支
+ +
第三个分支
不变 +

 

根据上面表格分析可知,每一次while循环无论进哪个分支条件,都是引起x或者x-y值的变大,两者最大值都是,所以最多有2次的循环,使他们都到达峰值

所以while循环这块的时间复杂度就是O(m)

综上所述,由于m<=n,所以整体KMP算法的时间复杂度就是O(n)(n是s1的长度)

posted on 2022-02-06 20:13  hqtyyds  阅读(534)  评论(2)    收藏  举报