编译原理实验(一):KMP算法

package KMP;

import java.util.ArrayList;
import java.util.List;

public class KMPTest {
    // 构造 pattern 的最大匹配数表
    public static int[] calculateMaxMatchLengths(String pattern) {
        int[] maxMatchLengths = new int[pattern.length()];
        int maxLength = 0;
        for (int i = 1; i < pattern.length(); i++) {
            while (maxLength > 0 && pattern.charAt(maxLength) != pattern.charAt(i)) {
                maxLength = maxMatchLengths[maxLength - 1]; // ①
            }
            if (pattern.charAt(i) == pattern.charAt(maxLength)) {
                maxLength++; // ②
            }
            maxMatchLengths[i] = maxLength;
        }
        return maxMatchLengths;
    }

    // 在 text 中寻找 pattern,返回所有匹配的位置开头
    public static List<Integer> search(String text, String pattern) {
        List<Integer> positions = new ArrayList<>();
        int[] maxMatchLengths = calculateMaxMatchLengths(pattern);
        int count = 0;
        for (int i = 0; i < text.length(); i++) {
            while (count > 0 && pattern.charAt(count) != text.charAt(i)) {
                count = maxMatchLengths[count - 1];
            }
            if (pattern.charAt(count) == text.charAt(i)) {
                count++;
            }
            if (count == pattern.length()) {
                positions.add(i - pattern.length() + 1);
                count = maxMatchLengths[count - 1];
            }
        }
        return positions;
    }

    public static void main(String[] args) {
        String text = "某某科技大学计算机学院软件工程";
        String patter = "计算机学院";
        List<Integer> list = search(text,patter);
        System.out.println(list);
    }
}


public class KMP {

    public static void main(String[] args) {
        String target = "abcdefg";
        String pattern = "cdefgh";
        boolean b = kmp(target, pattern);
        System.out.println(b);
    }

    /**
     * 使用 kmp算法 计算出 pattern 字符串是否 在 target的 子串中
     *
     * @param target  : 目标串
     * @param pattern : 模式串
     * @return true 表示存在,false 表示不存在
     */
    private static boolean kmp(String target, String pattern) {
        // 计算出部分匹配表
        int[] partialMathTable = createPartialMatchTable(pattern);

        char[] targetCharArr = target.toCharArray();
        char[] patternCharArr = pattern.toCharArray();
        int matchCharCounts = 0;// 记录下已经匹配的字符的个数

        int i = 0, j = 0, moveCounts = 0;
        while (i < targetCharArr.length) {
            // 如果当前主串和子串匹配上了,那么比较下一个字符是否匹配
            if (targetCharArr[i] == patternCharArr[j]) {
                matchCharCounts++;
                i++;
                j++;
            }
            // 如果子串的第一个元素都不和主串的元素相等,那么就拿主串的下一个元素进行比较
            else if (j == 0){
                i++;
            }
            // 如果子串不是在第一个元素而是在其他位置进行了匹配,那么进行移位操作
            else{
                // 移动位数 = 已匹配的字符串 - 对应部分匹配的值
                // 对应匹配值 指的是最后一个字符的对应配置值 j 是失配的位置 所以这里是partialMatchTable[j-1]
                moveCounts = matchCharCounts - partialMathTable[j -1];
                j = j-moveCounts; // 移动模式串 往前移 moveCounts
                matchCharCounts = matchCharCounts - moveCounts; // 修改匹配的字符个数,就是减去移动过去的位数
            }
            // 如果匹配成功了,直接返回true
            if (j == patternCharArr.length){
                return true;
            }
        }
        return false;
    }


    /**
     * 根据 pattern 字符串创建出对应的部分匹配表
     *
     * @param pattern : 字符串
     * @return 返回部分匹配表
     */
    private static int[] createPartialMatchTable(String pattern) {
        int patternLen = pattern.length();
        int[] matchTable = new int[patternLen];
        // 计算
        for (int i = 0, matchValue = 0; i < patternLen; i++) {
            if (i == 0) {
                matchValue = 0;
            } else {
                matchValue = calcMatchValue(pattern.substring(0, i + 1));
            }
            matchTable[i] = matchValue;
        }
        return matchTable;
    }

    // 计算一个字符串的部分匹配值 : 部分匹配值是前缀和后缀的最长共有元素
    private static int calcMatchValue(String substr) {
        int length = substr.length();
        // 前缀
        String preFixStr = substr.substring(0, length - 1);   // [0,length-1)
        // 后缀
        String suffixStr = substr.substring(1);                     // [1,length-1]

        while (preFixStr.length() > 0 && suffixStr.length() > 0) {
            if (preFixStr.equals(suffixStr)) {
                return preFixStr.length();
            }
            if (preFixStr.length() == 1 && suffixStr.length() == 1) {
                break;
            }
            // 前缀舍弃掉后边的一个字符,后缀舍弃掉前边的一个字符,再来匹配
            preFixStr = preFixStr.substring(0, preFixStr.length() - 1);
            suffixStr = suffixStr.substring(1, suffixStr.length());
        }
        return 0;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值