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中出现的次数。
总结
该程序通过 滚动哈希 和 快速幂算法 实现了一个高效的字符串匹配算法,避免了逐一比较子串的方式,从而优化了时间复杂度。在字符串较长或需要多次匹配时,这种方法尤其有效。
浙公网安备 33010602011771号