java实现回文算法以及KMP算法

package com.test.autimatic;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;

/**
 * @author 25338
 * @version 1.0
 * @date 2021/11/29 14:05
 * @description
 */
public class HiuWen {
    
    

    public static void main(String[] args) {
    
    
        //回文
        System.out.println(longestPalindrome("babad"));
        System.out.println(longestPalindrome1("babad"));
        //kmp
        KMP kmp = new KMP("ababab");
        System.out.println(kmp.search("ababab"));
        System.out.println(kmp.longestPrefix("abcababc"));
    }

    public static class KMP{
    
    
        //定义中间状态dp和要匹配的字符串pat(!!!第一维表示状态,第二维表示此时要匹配的字符串)
        int[][] dp;
        String pat;

        /**
         * 构造KMP匹配数据集合
         * @param pat
         */
        public KMP( String pat) {
    
    
            //初始化数据
            this.dp = new int[pat.length()][256];
            this.pat = pat;
            //定义初始化的最近回退状态
            int back = 0;
            //遍历数据
            for (int i = 0; i < pat.length(); i++) {
    
    
                //遍历所有字符
                System.arraycopy(dp[back], 0, dp[i], 0, 256);
                //back变化
                back = dp[i][pat.charAt(back) - '0'];
                //i位置上的目标值状态变化
                dp[i][pat.charAt(i) - '0'] = i + 1;
            }
        }

        /**
         * 此题完全仿照leetcode操作
         * https://leetcode-cn.com/problems/longest-happy-prefix/solution/zui-chang-kuai-le-qian-zhui-by-leetcode-solution/
         * @param s
         * @return
         */
        public String longestPrefix(String s) {
    
    
            //定义中间状态变量数组
            int n = s.length();
            int[] fail = new int[n];
            Arrays.fill(fail, -1);
            //遍历字符串
            for (int i = 1; i < n; ++i) {
    
    
                //从头开始匹配
                int j = fail[i - 1];
                //如果j不为-1就是不是初始状态并且此时字符不匹配则寻找前一个此字符匹配的位置j = fail [j]
                while (j != -1 && s.charAt(j + 1) != s.charAt(i)) {
    
    
                    j = fail[j];
                }
                //如果字符匹配到了--则状态值加一
                if (s.charAt(j + 1) == s.charAt(i)) {
    
    
                    fail[i] = j + 1;
                }
            }
            //返回最后一个状态值对应的长度
            return s.substring(0, fail[n - 1] + 1);
        }

        /**
         * 搜索
         * @param round
         * @return
         */
        public int search(String round){
    
    
            //定义初始状态值
            List<Integer> list = new ArrayList<>();
            int state = 0;
            //遍历要搜索的数据
            for (int i = 0; i < round.length(); i++) {
    
    
                //状态值得变化更新
                state = dp[state][round.charAt(i) - '0'];
                list.add(state);
                //如果达到目标长度最终状态
                if(state == pat.length()){
    
    
                    return i - state + 1;
                }
            }
            return -1;
        }
    }

    /**
     * 回文字符串的操作--manacher解法--leetcode
     * https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-by-leetcode-solution/
     * @param s
     * @return
     */
    public static String longestPalindrome1(String s) {
    
    
        //0.准备阶段,注意如果中心值是两个的话就需要转化--官方推荐每隔一个添加一个特殊字符
        StringBuilder joiner = new StringBuilder("#");
        for (int i = 0; i < s.length(); i++) {
    
    
            joiner.append(s.charAt(i));
            joiner.append("#");
        }
        s = joiner.toString();
        //1.定义存储已经遍历过的位置的字符的臂长
        List<Integer> list_arm = new ArrayList<>();
        //定义:::前一个扩散中心,中心位置+臂长,最终结果的开始,结束位置
        int before_site = 0,tem_len = 0,start = 0,end = 0;
        //从前向后遍历字符串
        for (int i = 0; i < s.length(); i++) {
    
    
            int arm;
            //判断前一个字符的位置加臂长是否大于i--此处即可发现我们需要定义两个变量:位置+臂长;位置;臂长已经存储在list内里
            if(tem_len > i){
    
    
                //由官方推论可知;i位置关于j位置对称坐标为 2*j - i;因此i位置的臂长至少为 2* j -i位置的臂长;
                //或者是 j 位置+j的臂长- 减去i的下标位置。
                int temp_arm = Math.min(list_arm.get(2 * before_site - i), tem_len - i);
                //从臂长的位置分别向两端扩展获得新的臂长
                int left = i - temp_arm,right = i + temp_arm;
                while (left > -1 && right < s.length() && s.charAt(left) == s.charAt(right)){
    
    left--;right++;}
                //由于左右都多做了一次操作所以减去2
                arm = (right - left - 2) / 2;
            }else{
    
    //直接计算臂长即可
                int left = i - 1,right = i + 1;
                while (left > -1 && right < s.length() && s.charAt(left) == s.charAt(right)){
    
    left--;right++;}
                arm = (right - left - 2) / 2;
            }
            //将臂长加入集合
            list_arm.add(arm);
            //判断位置加臂长是否大于前一个位置加臂长,大于的话替换掉-位置,臂长+位置两个变量
            if(arm + i > tem_len){
    
    
                before_site = i;//位置替换
                tem_len = arm + i;//臂长+位置的变量替换
            }
            //判断回文长度的大小是否替换--如果2倍的臂长加上中心值1大于end-start,则替换
            if(2 * arm + 1 > end - start){
    
    
                start = i - arm;
                end = i + arm;
            }
        }
        //最后获取数据
        StringBuilder builder = new StringBuilder();
        for (int i = start; i <= end; i++) {
    
    
            if(s.charAt(i) != '#'){
    
    
                builder.append(s.charAt(i));
            }
        }
        return builder.toString();
    }
        /**
         * 最长回文字符串--暴力解法
         * @param s
         * @return
         */
    public static String longestPalindrome(String s) {
    
    
        //记录最长字符串-起始位置
        int start = 0, end = 0;
        //循环字符数组
        for (int i = 1; i < s.length() - 1; i++) {
    
    
            //从第二个字符开始--
            //情况一:如果以此字符为中心向两端扩散直到两端小于边界值或不相等为止
            int tempS = i - 1,tempE = i + 1;
            while (tempS >= 0 && tempE < s.length() && s.charAt(tempS) == s.charAt(tempE)){
    
     tempS--;tempE++; }
            //如果截取回文长度大于之前的存储的回文长度,则替换开始,结束值
            if(--tempE - ++tempS > end - start){
    
     start = tempS;end = tempE; }
            //重置tempS,tempE;
            tempE = i;tempS = i - 1;
            //情况二:以此字符和他前一个字符两个字符为中心扩散-终止条件同上
            while (tempS >= 0 && tempE < s.length() && s.charAt(tempS) == s.charAt(tempE)){
    
     tempS--;tempE++; }
            //如果截取回文长度大于之前的存储的回文长度,则替换开始,结束值
            if(--tempE - ++tempS > end - start){
    
     start = tempS;end = tempE; }
        }
        return s.substring(start,end+1);
    }

}

猜你喜欢

转载自blog.csdn.net/weixin_43795840/article/details/121621629