1.java内置函数

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str1=sc.nextLine();
        String str2=sc.next();
        String str3=str2.replaceAll(str1,"");
        int n=str1.length()-str3.length();
        int m=n/str1.length();
        System.out.println(m);
    }
}

2.KMP

import java.util.*;
import java.io.*;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static int ans = 0;
    public static void main(String[] args) throws IOException {
        char[] s = in.readLine().toCharArray();
        char[] t = in.readLine().toCharArray();
        int n = s.length;
        int m = t.length;
        //寻找字串中相同前后缀的长度
        int[] next = new int[n];
        //注意:i从1开始,和j不一样,要不然没有比对的价值
        for(int i = 1, j = 0; i < n; i++) {
            //回退
            while(j > 0 && s[i] != s[j]) {
                j = next[j - 1];
            }
            if(s[i] == s[j]) j++;
            next[i] = j;
        }
        for(int i = 0, j = 0; i < m; i++) {
            //拟合的过程中,不符合情况就根据next数组回退
            while(j > 0 && s[j] != t[i]) {
                j = next[j - 1];
            }
            //如果要是配上对了,就指向子串的指针j向前移动
            if(s[j] == t[i]) {
                j++;
            }
            //注意:匹配成功时,不仅ans++,j也要归零,下一个开始时重新比对
            if(j == n) {
                ans++;
                j = 0;
            }
        }
        System.out.println(ans);
    }
}

3.滑动窗口

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main{
    public static void main(String[] args) throws IOException {
        //输入流处理学习一下
        //使用了一个较大的缓冲区 (8192 << 9),并通过 BufferedReader 读取文本输入
        BufferedReader bf=new BufferedReader(new InputStreamReader(new BufferedInputStream(System.in,8192<<9)),8192<<9);
        String str1=bf.readLine();
        String str2=bf.readLine();
        int res=0;
        //创建了一个 StringBuilder 对象,初始容量为 s1 的长度,用来拼接和修改字符串。
        StringBuffer sb=new StringBuffer(str1.length());
        if(sb.append(str2.substring(0,str1.length())).toString().equals(str1)) res++;
        char[] ch=str2.toCharArray();
        for(int i=str1.length();i<ch.length;i++){
            if(sb.deleteCharAt(0).append(ch[i]).toString().equals(str1)) res++;
        }
        System.out.println(res);
    }
}
 //其实我感觉滑动窗口的核心思想就是队列,头尾进行操作来使窗口滑动

4.hash

 

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    
    static BufferedReader br = new BufferedReader(new InputStreamReader(new BufferedInputStream(System.in, 8192 << 9)), 8192 << 8);

    public static void main(String[] args) throws IOException {
        char[] s1 = br.readLine().toCharArray();
        char[] s2 = br.readLine().toCharArray();
        int n = s1.length;
        int m = s2.length;
        long base = 2; //prime
        long target = 0; //target hash
        for (int i = 0; i < n; ++i) target = target * base + s1[i] - 'A' + 1;
        long[] h = new long[m + 1]; // main string hash
        for (int i = 1; i <= m; ++i) h[i] = h[i - 1] * base + s2[i - 1] - 'A' + 1;
        // 计算 base 的目标字串 n 长度次幂
        long b = 1, d = base, p = n;
        while (p != 0) {
            if ((p & 1) == 1) b = b * d;
            d = d * d;
            p >>>= 1;
        }
        //计算结果
        int res = 0;
        for (int l = 0, r = l + n; r <= m; ++l, ++r) { //LR
            long sum = h[r] - h[l] * b;
            if (sum == target) res++;
        }
        System.out.println(res);
    }

}

5.常规解法(也是滑动窗口)

import java.util.Scanner;

public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str1 = sc.nextLine();
        String str2 = sc.nextLine();
        char[] s1 = str1.toCharArray();
        char[] s2 = str2.toCharArray();
        int count=0;
        for(int i=0;i<s2.length;i++){
            int temp=i;
            for(int j=0;j<s1.length;j++){
                if(s2[temp]==s1[j]) {
                    temp++;
                    if(j==s1.length-1) count++;
                }
                else break;
            }
        }
        System.out.println(count);
    }
}

 HASH

这段代码的目的是通过 滚动哈希算法 实现一个字符串匹配的功能,即统计一个字符串(s1)在另一个字符串(s2)中出现的次数。以下是对代码的详细解释:

1. 输入部分

javaCopy Code
char[] s1 = br.readLine().toCharArray();
char[] s2 = br.readLine().toCharArray();
int n = s1.length;
int m = s2.length;
  • 读取输入字符串 s1 和 s2,并将其转换为字符数组。
  • n 和 m 分别是 s1 和 s2 的长度。

2. 计算目标字符串的哈希值

javaCopy Code
long base = 2; // prime number as base for hash calculation
long target = 0; // target hash of s1
for (int i = 0; i < n; ++i) target = target * base + s1[i] - 'A' + 1;
  • base 是计算哈希值的基数,这里选用了一个质数 2
  • target 是目标字符串 s1 的哈希值。哈希计算是通过将每个字符的 ASCII 值减去 'A' 后加 1 进行的,然后将结果依次乘以 base 进行累加。该哈希值最终用于后续的匹配比较。

3. 计算主字符串的哈希值

javaCopy Code
long[] h = new long[m + 1]; // Hash array for s2
for (int i = 1; i <= m; ++i) h[i] = h[i - 1] * base + s2[i - 1] - 'A' + 1;
  • 计算字符串 s2 中每个前缀的哈希值,并将结果存储在数组 h 中。h[i] 表示 s2 从第 0 到第 i-1 个字符的哈希值,使用的公式是:

    h[i]=h[i−1]∗base+value of s2[i-1]

  • 这里通过递推的方式,高效地计算出每个前缀的哈希值。

4. 计算 base^n(即目标字符串的长度的幂)

javaCopy Code
long b = 1, d = base, p = n;
while (p != 0) {
    if ((p & 1) == 1) b = b * d;
    d = d * d;
    p >>>= 1;
}
  • 计算 base^n,即 base 的 n 次幂。这里使用了 快速幂算法,通过二进制拆分的方式来高效计算大数的幂。

5. 滑动窗口和哈希匹配

javaCopy Code
int res = 0;
for (int l = 0, r = l + n; r <= m; ++l, ++r) { // L-R pointer
    long sum = h[r] - h[l] * b;
    if (sum == target) res++;
}
  • 这里使用了滑动窗口技术,l 和 r 分别表示当前窗口的左右边界,r 是右边界,l 是左边界。每次窗口的长度保持为 n(即目标字符串的长度),窗口从 s2 中滑动。
  • 对于每个窗口(从 s2[l] 到 s2[r-1]),通过哈希值的差值 h[r] - h[l] * b 来计算当前窗口的哈希值,比较它是否等于 target(即 s1 的哈希值)。
  • 如果匹配成功,res 计数器加 1。

6. 输出结果

javaCopy Code
System.out.println(res);
  • 最后,输出匹配的次数 res,即目标字符串 s1 在主字符串 s2 中出现的次数。

总结

该程序通过 滚动哈希快速幂算法 实现了一个高效的字符串匹配算法,避免了逐一比较子串的方式,从而优化了时间复杂度。在字符串较长或需要多次匹配时,这种方法尤其有效。

posted on 2025-06-02 15:29  fafrkvit  阅读(8)  评论(0)    收藏  举报