KMP算法查询字符串
package 字符串;
/**
* @author: DreamCode
* @file: KMP.java
* @time: 2022年3月18日-下午7:28:08
* @思路: KMP算法查询字符串
*/
public class KMP {
/**
* KMP算法查询字符串
*/
public static void main(String[] args) {
String src = "babababcbabababb";
int index = indexOf(src, "c");
System.out.println(index);
}
private static int indexOf(String s, String p) {
// TODO 在s字符串中查询p字符串
// 初始化next数组
if(p.length()==0) {
return -1;
}
int[] next = get_next(p);
int i=0,j=0;
while(i<s.length()) {
if(j==-1||s.charAt(i)==p.charAt(j)) {
i++;
j++;
}else {
j=next[j];
}
if(j==p.length()) {
return i-j;
}
}
return -1;
}
private static int[] get_next(String p) {
// TODO 获取p字符串的next数组
int len = p.length();
int[] next = new int[len+1];
next[0] = -1; //第0个字符的最长匹配前缀后缀为-1
if(len==0) {
return next;
}
next[1] = 0; //第1个字符的最长匹配前缀后缀为0
int j=1; //从第1个字符逐步递推next数组
int k=next[j]; //k为记录最长的匹配前缀后缀长度的位置
while(j<len) {
if(k<0||p.charAt(k)==p.charAt(j)) {
next[++j]=++k;
}else {
k=next[k];
}
}
return next;
}
}
KMP_next数组的应用
For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive), we want to know whether the prefix is a periodic string. That is, for each i (2 <= i <= N) we want to know the largest K > 1 (if there is one) such that the prefix of S with length i can be written as AK ,that is A concatenated K times, for some string A. Of course, we also want to know the period K.
@input: The input consists of several test cases. Each test case consists of two lines. The first one contains N (2 <= N <= 1 000 000) – the size of the string S.The second line contains the string S. The input file ends with a line, having the number zero on it.
@output:For each test case, output “Test case #” and the consecutive test case number on a single line; then, for each prefix with length i that has a period K > 1, output the prefix size i and the period K separated by a single space; the prefix sizes must be in increasing order. Print a blank line after each test case.
package 字符串;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
/**
* @author: DreamCode
* @file: Period.java
* @time: 2022年3月19日-上午11:38:55
* @思路: 字符串呈现周期性:i%(i-k)==0,周期次数为i/(i-k);i为当前匹配的位置,k为前i-1位最长前缀后缀匹配长度(next[i])
*/
public class next数组的应用_Period {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
List<String> list = new LinkedList<>();
while (true) {
int n = scanner.nextInt();
if (n == 0)
break;
String string = scanner.next();
list.add(string);
}
for (int i = 0; i < list.size(); i++) {
String string = list.get(i);
System.out.println("Test case #" + (i + 1));
int[] next = get_next(string);
for (int j = 2; j < next.length; j++) {
int k = next[j]; //k为当前匹配位置的next值
int d = j - k;
if (j % d == 0 && j / d > 1) { // 呈现周期性
int count = j / d;
System.out.println(j + " " + count);
}
}
System.out.println();
}
}
private static int[] get_next(String string) {
// TODO 获取字符串的next数组
if (string == null || string.length() == 0)
return null;
int len = string.length();
int[] next = new int[len + 1];
next[0] = -1;
if (len == 1)
return next;
next[1] = 0;
int i = 1;
int k = next[i];
while (i < len) {
if (k == -1 || string.charAt(i) == string.charAt(k))
next[++i] = ++k;
else
k = next[k];
}
return next;
}
}
单词翻转
package 字符串;
/**
* @author: DreamCode
* @file: 单词翻转.java
* @time: 2022年3月17日-下午12:50:11
* @思路: 先将整体字符串翻转,然后将每一个单词再做翻转
*/
public class 单词翻转 {
/**
* 将字符串按单词翻转,如here you are 翻转成are you here
*/
public static void main(String[] args) {
String ans = reverse("");
System.out.println(ans);
}
private static String reverse(String string) {
string = reverseString(string);
String[] strArrayStrings = string.split("\\s");
StringBuilder ansStringBuilder = new StringBuilder();
for(String str:strArrayStrings) {
str = reverseString(str);
ansStringBuilder.append(str+" ");
}
return ansStringBuilder.deleteCharAt(ansStringBuilder.length()-1).toString();
}
private static String reverseString(String string) {
StringBuilder stringBuilder = new StringBuilder(string);
return stringBuilder.reverse().toString();
}
}
后缀数组的应用_最长公共字串
给出一个字符串 S,考虑其所有重复子串(S 的连续子串,出现两次或多次,可能会有重叠)。 返回任何具有最长可能长度的重复子串。(如果 S不含重复子串,那么答案为 “”。
package 字符串;
import java.util.Arrays;
/**
* @author: DreamCode
* @file: 后缀数组的应用_最长公共字串.java
* @time: 2022年3月19日-下午3:56:45
* @思路: 采用后缀数组法,经过排序后,最长公共字串必然存在相邻的两个后缀字串中,求出最大的公共字串长度与index即可
*/
public class 后缀数组的应用_最长公共字串 {
public static void main(String[] args) {
String string = "123232";
String res = longestDupSubstring(string);
System.out.println(res);
}
private static String longestDupSubstring(String string) {
// TODO 求最长公共字串
//构造后缀数组
int len = string.length();
String[] arr = new String[len];
for(int i=0;i<len;i++) {
String subString = string.substring(i);
arr[i]=subString;
}
//对后缀数组进行排序
Arrays.sort(arr);
//寻找相邻两个字串的最长公共数组高度
int maxLen = -1;
String res="";
for(int i=0;i<arr.length-1;i++) { //构造高度
int commonLen = getCommonLen(arr[i],arr[i+1]); //高度
if(commonLen>maxLen) { //更新最长公共字串
maxLen=commonLen;
res=arr[i].substring(0,maxLen);
}
}
return res;
}
private static int getCommonLen(String string, String string2) {
// TODO 求两个字符串的公共字串
int count=0;
int len = Math.min(string.length(), string2.length());
for(int i=0;i<len;i++) {
if(string.charAt(i)==string2.charAt(i)) {
count++;
}else {
break;
}
}
return count;
}
}
回文字符串
package 字符串;
/**
* @author: DreamCode
* @file: 回文字符串.java
* @time: 2022年3月17日-下午8:52:48
* @思路: 翻转后和自己相同---回文字符串
*/
public class 回文字符串 {
/**
* 判断字符串是否为回文字符串
*/
public static void main(String[] args) {
String testString = " 1 ";
boolean ans = isPalindrome(testString);
System.out.println(ans);
}
private static boolean isPalindrome(String testString) {
if(testString=="") {
return true;
}
return testString.equals(new StringBuffer(testString).reverse().toString());
}
}
判断两字符串的字符集是否相等
package 字符串;
import java.util.HashMap;
import java.util.Map;
/**
* @author: DreamCode
* @file: 判断两字符串的字符集是否相等.java
* @time: 2022年3月17日-上午11:43:57
* @思路: Map容器(索引)的运用
*/
public class 判断两字符串的字符集是否相等 {
/**
* 询问两个串是否由相同的字符集生成
*/
public static void main(String[] args) {
boolean ans = isequal("abcdfv","afdbc");
System.out.println(ans);
}
private static boolean isequal(String string, String string2) {
// TODO 询问两个串是否由相同的字符集生成
Map<Character, Integer> record = new HashMap<>();
for(Character character:string.toCharArray()) {
if(record.get(character)==null) {
record.put(character, 1);
}
}
for(Character character:string2.toCharArray()) {
if(record.get(character)==null) {
return false;
}
}
return true;
}
}
旋转词
package 字符串;
/**
* @author: DreamCode
* @file: 旋转词.java
* @time: 2022年3月17日-下午12:14:39
* @思路: 如果a是b的一个旋转词,那么b+b一定包含a(比如12341234 包含2341,故2341是1234的一个旋转词)
*/
public class 旋转词 {
/**
* 旋转词的意思是:1234 2341 3412 把左边的任意一部分移到右边去
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
boolean ans = isRotate("defa","fabdde");
System.out.println(ans);
}
private static boolean isRotate(String string2, String string) {
if(string.length()!=string2.length()) {
return false;
}
String string3 = string+string;
if(string3.contains(string2)) {
return true;
}
return false;
}
}
移除字符串中连续出现的K个0
package 字符串;
/**
* @author: DreamCode
* @file: 移除字符串中连续出现的K个0.java
* @time: 2022年3月17日-下午8:39:33
* @思路: 可以用扫描字符数组的解法,但是用正则表达式更为快捷
*/
public class 移除字符串中连续出现的K个0 {
public static void main(String[] args) {
String testString="A00000B00";
int k=2;
String ansString = removeKZero(testString, k);
System.out.println(ansString);
}
private static String removeKZero(String testString, int k) {
String regexString = "0{"+ k + "}";
return testString.replaceAll(regexString,"");
}
}
最短摘要_尺取法
Alibaba笔试题:给定一段产品的英文描述,包含M个英文字母,每个英文单词以空格分隔,无其他标点符号;再给定N个英文单词关键字,请说明思路并编程实现方法,目标是找出此产品描述中包含N个关键字(每个关键词至少出现一次)的长度最短的子串,作为产品简介输出。
package 字符串;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @author: DreamCode
* @file: 最短摘要_尺取法.java
* @time: 2022年3月17日-下午9:26:14
* @思路: 尺取法,每一次满足约束条件,判断尺子的长度,若更小则更新答案
*/
public class 最短摘要_尺取法 {
public static void main(String[] args) {
String[] strArrays = { "a", "b", "c", "seed", "h", "e", "f", "c", "c", "seed", "e", "f", "seed", "c" };
String[] keyArray = { "c","seed","c","c" };
solve(strArrays, keyArray);
}
private static void solve(String[] strArrays, String[] keyArray) {
int begin = -1;
int end = -1;
int len = Integer.MAX_VALUE;
int p = -1;
for (int i = 0; i < strArrays.length; i++) {
// 判断当前字符是否是key
if (find(strArrays[i], keyArray) != -1) {// 当前字符串为关键字,找到尺子左边界
int j;
if (p != -1) { // 尺取法,若上一次有取到,则下一次的j为p
j = p;
} else {
j = i + 1; // 第一次的情况处理(第一个尺子)
}
for (; j < strArrays.length; j++) { // 寻找尺子右边界
if (find(strArrays[j], keyArray) != -1) { // 右指针字符为关键字
if (containtAll(strArrays, keyArray, i, j)) { // 当前包含所有关键字
if (j - i + 1 < len) { // 当前为最短串,进行更新
len = j - i + 1;
begin = i;
end = j;
}
break;
}
}
}
}
}
print(strArrays, begin, end);
}
private static void print(String[] strArrays, int begin, int end) {
System.out.println(begin+" "+end);
// TODO 打印最短字串
for (int i = begin; i <= end; i++) {
System.out.print(strArrays[i] + " ");
}
}
private static boolean containtAll(String[] strArrays, String[] keyArray, int begin, int end) {
// TODO 判断在strArrays的i到j的位置是否包含所有的keyArray
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < keyArray.length; i++) { // 构建key的hash表
if (map.get(keyArray[i]) == null) {
map.put(keyArray[i], 1);
} else {
map.put(keyArray[i], map.get(keyArray[i]) + 1);
}
}
Map<String, Integer> mapRecord = new HashMap<>();
for (int i = begin; i <= end; i++) { // 都见str的hash表
if (mapRecord.get(strArrays[i]) == null) {
mapRecord.put(strArrays[i], 1);
} else {
mapRecord.put(strArrays[i], mapRecord.get(strArrays[i]) + 1);
}
}
// 判断两个hash表的内容是否满足要求
for (Map.Entry<String, Integer> e : map.entrySet()) {
if (mapRecord.get(e.getKey()) == null || mapRecord.get(e.getKey()) != e.getValue()) {
return false;
}
}
return true;
}
private static int find(String string, String[] keyArray) {
for (int i = 0; i < keyArray.length; i++) {
if (string.equals(keyArray[i])) {
return i;
}
}
return -1;
}
}