字符串问题--KMP ,朴素字符串匹配;manacher算法;

五KMPO(N)

子串和子数组都是连续的;子序列可以不是连续的;

(1)朴素的模式匹配算法:O(n*M)

indexOf()底层和KMP几乎一样;只是没有匹配到的时候,返回-1;

public class KMP1 {
    public static void main(String[] args) {
        String str = "abcrr123tyeererre";
        String s = "123";
        System.out.println(process(str, s));
        //indexOf()
        int aaa = str.indexOf(s);
        System.out.println(aaa);


    }
    //朴素匹配算法
    public static int process(String str,String s){
        int i=0;//i为最后匹配到的在str上的位置;
        int j=0;
        int a=-1;//为s与str匹配上的在str的第一个字符的位置;
        while(i<str.length()&&j<s.length()){
            if (str.charAt(i)==s.charAt(j)){

                i++;
                j++;
            }else {
                i=i-j+1;
                j=0;
            }
            if (j>=s.length()-1){
                a= i-j;

                break;
            }

        }
        return a;


    }




}

(2) 给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串?

题目:
// 给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串?
// 比如 s1 = “abcde”,s2 = “axbc”
// 返回 1

给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串?
	// 比如 s1 = "abcde",s2 = "axbc"
	// 返回 1

	// 解法一,来自群里的解法:
	// 求出str2所有的子序列,然后按照长度排序,长度大的排在前面。
	// 然后考察哪个子序列字符串和s1的某个子串相等(KMP),答案就出来了。
	// 分析:
	// 因为题目原本的样本数据中,有特别说明s2的长度很小。所以这么做也没有太大问题,也几乎不会超时。
	// 但是如果某一次考试给定的s2长度远大于s1,这么做就不合适了。
	public static int minCost1(String s1, String s2) {
		List<String> s2Subs = new ArrayList<>();
		process(s2.toCharArray(), 0, "", s2Subs);
		s2Subs.sort(new LenComp());
		for (String str : s2Subs) {
			if (s1.indexOf(str) != -1) { // indexOf底层和KMP算法代价几乎一样,也可以用KMP代替
				return s2.length() - str.length();
			}
		}
		return s2.length();
	}

	public static void process(char[] str2, int index, String path, List<String> list) {
		if (index == str2.length) {
			list.add(path);
			return;
		}
		process(str2, index + 1, path, list);
		process(str2, index + 1, path + str2[index], list);
	}

	public static class LenComp implements Comparator<String> {

		@Override
		public int compare(String o1, String o2) {
			return o2.length() - o1.length();
		}

	}

KMP O(N) 求需要匹配的字符串str2在str1的位置下标;
public static int kmp(String str, String dest){
        //1.首先计算出 部分匹配表
        int[] next = kmpnext(dest);

        System.out.println("next ="+Arrays.toString(next));
        //2.查找匹配位置
        for(int i = 0, j = 0; i < str.length(); i++){
            while(j > 0 && str.charAt(i) != dest.charAt(j)){
                j = next[j-1];
            }
            if(str.charAt(i) == dest.charAt(j)){
                j++;
            }
            if(j == dest.length()){
                return i-j+1;
            }
        }
        return -1;
    }

    /**
     * 计算部分匹配表
     */
    public static int[] kmpnext(String dest){
        int[] next = new int[dest.length()];
        next[0] = 0;

        for(int i = 1,j = 0; i < dest.length(); i++){
            while(j > 0 && dest.charAt(j) != dest.charAt(i)){
                j = next[j - 1];
            }
            if(dest.charAt(i) == dest.charAt(j)){
                j++;
            }
            next[i] = j;
        }
        return next;
    }
Mancher马拉车O(N):最长回文子串的长度:O(N)

如果是最长回文子串是那个;看力扣解题;o(N2);用动态规划解题的,也能用马拉车解;

也可以用中心扩展法,加上#号后,求长度;能达到o(N2) 可以求长度或者回文子串;

 

public static char[] manacherString(String str){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            sb.append("#");
            sb.append(str.charAt(i));
        }
        sb.append("#");
        return sb.toString().toCharArray();
    }
//直接调用这个函数就行;求出最长的子串长度;
    public static int manacher(String str){
        if(str == null || str.length() < 1){
            return 0;
        }
        char[] charArr = manacherString(str);
        int[] radius = new int[charArr.length];
        int R = -1;
        int c = -1;
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < radius.length; i++) {
            radius[i] = R > i ? Math.min(radius[2*c-i],R-i+1):1;
            while(i+radius[i] < charArr.length && i - radius[i] > -1){
                if(charArr[i-radius[i]] == charArr[i+radius[i]]){
                    radius[i]++;
                }else{
                    break;
                }
            }
            if(i + radius[i] > R){
                R = i + radius[i]-1;
                c = i;
            }
            max = Math.max(max,radius[i]);
        } 
        return max-1;
    }
(2)修改–得到最长回文子串长度
小A非常喜欢回文串,当然我们都知道回文串这种情况是非常特殊的。所以小A只想知道给定的一个字符串的最大回文子串是多少,但是小A对这个结果并不是非常满意。现在小A可以对这个字符串做一些改动,他可以把这个字符串最前面的某一段连续的字符(不改变顺序)移动到原先字符串的末尾。那么请问小A通过这样的操作之后(也可以选择不移动)能够得到最大回文子串的长度是多少。 
import java.util.*;
 
public class Main {
    public static void main(String... args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        String str = s + s;
        int ans = manacher(str);
        //ans = Math.min(ans, s.length());
        System.out.println(ans);
    }
     

public static char[] manacherString(String str){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            sb.append("#");
            sb.append(str.charAt(i));
        }
        sb.append("#");
        return sb.toString().toCharArray();
    }
//直接调用这个函数就行;求出最长的子串长度;
    public static int manacher(String str){
        if(str == null || str.length() < 1){
            return 0;
        }
        char[] charArr = manacherString(str);
        int[] radius = new int[charArr.length];
        int R = -1;
        int c = -1;
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < radius.length; i++) {
            radius[i] = R > i ? Math.min(radius[2*c-i],R-i+1):1;
            while(i+radius[i] < charArr.length && i - radius[i] > -1){
                if(charArr[i-radius[i]] == charArr[i+radius[i]]){
                    radius[i]++;
                }else{
                    break;
                }
            }
            if(i + radius[i] > R){
                R = i + radius[i]-1;
                c = i;
            }
            max = Math.max(max,radius[i]);
        } 
        return max-1;
    }
}
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值