问题发现
当我在使用正则表达式解决重复匹配字符串时发现,当开头和结尾是同一个字符时且结尾的字符又作为开头,正则表达式无法使用(或许有办法使用,只是我不知道,希望了解的人可以补充),但是传统的字符串匹配方法又无法快速地处理较大的文本,所以我想到了KMP算法,或许可以利用其来解决这个特定的问题,众所周知,KMP算法的效率十分的好,相较于传统的字符串匹配可以更加的快速来匹配子串。
普通KMP算法
import java.util.ArrayList;
public class UsingKMPAlgorithmSpiltString {
public static void main(String[] args) {
//待分割的文本
String text = "【分割符号】劳力士【分割符号】浪琴【分割符号】欧米茄【分割符号】宝珀【分割符号】百达翡丽" ;
//分割符号字符串
String pattern = "【分割符号】";
UsingKMPAlgorithmSpiltString kmp = new UsingKMPAlgorithmSpiltString();
ArrayList<Integer> matchIndices = kmp.KMPSearch(text, pattern);
for (int i = 0; i < matchIndices.size(); i++) {
if (i + 1 < matchIndices.size()) {
System.out.println(text.substring(matchIndices.get(i), matchIndices.get(i + 1)));
} else {
System.out.println(text.substring(matchIndices.get(i)));
}
}
}
// 计算部分匹配表(Partial Match Table)
private int[] computeLPSArray(String pattern) {
int[] lps = new int[pattern.length()];
int len = 0; // 长度为已匹配的前缀和后缀的长度
int i = 1; // lps[0]总是0,所以从1开始
while (i < pattern.length()) {
if (pattern.charAt(i) == pattern.charAt(len)) {
len++;
lps[i] = len;
i++;
} else {
if (len != 0) {
len = lps[len - 1]; // 不匹配时减少len
} else {
lps[i] = 0; // 如果没有匹配到前缀,lps[i]为0
i++;
}
}
}
return lps;
}
// KMP 算法实现
public ArrayList<Integer> KMPSearch(String text, String pattern) {
ArrayList<Integer> matchIndices = new ArrayList<>();
int textLength = text.length();
int patternLength = pattern.length();
int[] lps = computeLPSArray(pattern);
int i = 0; // text 中的索引
int j = 0; // pattern 中的索引
while (i < textLength) {
if (pattern.charAt(j) == text.charAt(i)) {
i++;
j++;
}
if (j == patternLength) {
matchIndices.add(i - j);
j = lps[j - 1];
} else if (i < textLength && pattern.charAt(j) != text.charAt(i)) {
if (j != 0) {
j = lps[j - 1];
} else {
i++;
}
}
}
return matchIndices;
}
}
输出结果:
【分割符号】劳力士
【分割符号】浪琴
【分割符号】欧米茄
【分割符号】宝珀
【分割符号】百达翡丽
多模式KMP算法
使用两个进行分割,只要分隔符属于这两个中的其中一个就可以实现分割
import java.util.ArrayList;
import java.util.List;
public class MultimodalKMPAlgorithmSplitString {
public static void main(String[] args) {
String text = "离人世,有分也须相遇。约十洲三岛,骖鸾跨鹤,大家同去。 |陈维崧《湖海楼词〖DK〗·凄凉犯〖DK〗·哭云间友人金蓬山》(《清名家词》二〖JP〗172〖JP5〗):玉京伴侣,似人间,也愁离索。促返蓬山,果然去,骖鸾跨鹤。\uE005" +
"又作〖HTH〗〖BF〗[〖BFB〗骖鸾驭凤〖BF〗]〖BFB〗〖HT〗。武则天《升仙太子碑序》(《全唐文》九八):骖鸾驭凤,升八景而戏仙庭;驾月乘云,驱百灵而朝上帝。\uE003" +
"又作〖HTH〗〖BF〗[〖BFB〗骖虬驭鹤〖BF〗]〖BFB〗〖HT〗,虬〖WTXT〗〖JP〗(qiú)〖JP5〗〖WT〗:有角的小龙;龙。杜光庭《罗天醮岳渎词》(《全唐文》九三八):仙坛灵化,皆骖虬驭鹤之踪。\uE004" +
"又作〖HTH〗〖BF〗[〖BFB〗鹤驾鸾骖〖BF〗]〖BFB〗〖HT〗。清〖DK〗·褚人穫《坚瓠集〖DK〗·五集〖DK〗·三〖DK〗·神仙太守》:古今何处有神仙,鹤驾鸾骖总浪传。\uE003" +
"又作〖HTH〗〖BF〗[〖BFB〗驾鹤骖鸾〖BF〗]〖BFB〗〖HT〗。张大直《题莲华西洞二首(其二)》(《宋诗纪事续补》二五):真人隐壁君休问,驾鹤骖鸾自有时。\uE004" +
"又作〖HTH〗〖BF〗[〖BFB〗控鹤骖鸾〖BF〗]〖BFB〗〖HT〗。王瞻《高盖名山院记》(《全唐文》九○○):是谓控鹤骖鸾之客,以九仙六洞为家;出生离死之人,以大道三界为宅。\uE004";
getPara(text,"\uE003","\uE004");
}
public static void getPara(String text,String pattern1,String pattern2) {
// 分割符号字符串
MultimodalKMPAlgorithmSplitString kmp = new MultimodalKMPAlgorithmSplitString();
ArrayList<Integer> matchIndices = kmp.multiPatternKMPSearch(text, pattern1, pattern2);
if (matchIndices.size() > 0) {
System.out.println(text.substring(0, matchIndices.get(0)));
for (int i = 0; i < matchIndices.size(); i++) {
if (i + 1 < matchIndices.size()) {
System.out.println(text.substring(matchIndices.get(i) + 1, matchIndices.get(i + 1)));
} else {
System.out.println(text.substring(matchIndices.get(i) + 1));
}
}
}
}
// 计算部分匹配表(Partial Match Table)
private int[] computeLPSArray(String pattern) {
int[] lps = new int[pattern.length()];
int len = 0; // 长度为已匹配的前缀和后缀的长度
int i = 1; // lps[0]总是0,所以从1开始
while (i < pattern.length()) {
if (pattern.charAt(i) == pattern.charAt(len)) {
len++;
lps[i] = len;
i++;
} else {
if (len != 0) {
len = lps[len - 1]; // 不匹配时减少len
} else {
lps[i] = 0; // 如果没有匹配到前缀,lps[i]为0
i++;
}
}
}
return lps;
}
// 多模式 KMP 算法实现
public ArrayList<Integer> multiPatternKMPSearch(String text, String pattern1, String pattern2) {
ArrayList<Integer> matchIndices = new ArrayList<>();
List<String> patterns = new ArrayList<>();
patterns.add(pattern1);
patterns.add(pattern2);
for (String pattern : patterns) {
int textLength = text.length();
int patternLength = pattern.length();
int[] lps = computeLPSArray(pattern);
int i = 0; // text 中的索引
int j = 0; // pattern 中的索引
while (i < textLength) {
if (pattern.charAt(j) == text.charAt(i)) {
i++;
j++;
}
if (j == patternLength) {
matchIndices.add(i - j);
j = lps[j - 1];
} else if (i < textLength && pattern.charAt(j) != text.charAt(i)) {
if (j != 0) {
j = lps[j - 1];
} else {
i++;
}
}
}
}
// 对匹配结果排序
matchIndices.sort(Integer::compareTo);
return matchIndices;
}
}
输出结果:
离人世,有分也须相遇。约十洲三岛,骖鸾跨鹤,大家同去。 |陈维崧《湖海楼词〖DK〗·凄凉犯〖DK〗·哭云间友人金蓬山》(《清名家词》二〖JP〗172〖JP5〗):玉京伴侣,似人间,也愁离索。促返蓬山,果然去,骖鸾跨鹤。又作〖HTH〗〖BF〗[〖BFB〗骖鸾驭凤〖BF〗]〖BFB〗〖HT〗。武则天《升仙太子碑序》(《全唐文》九八):骖鸾驭凤,升八景而戏仙庭;驾月乘云,驱百灵而朝上帝。
又作〖HTH〗〖BF〗[〖BFB〗骖虬驭鹤〖BF〗]〖BFB〗〖HT〗,虬〖WTXT〗〖JP〗(qiú)〖JP5〗〖WT〗:有角的小龙;龙。杜光庭《罗天醮岳渎词》(《全唐文》九三八):仙坛灵化,皆骖虬驭鹤之踪。
又作〖HTH〗〖BF〗[〖BFB〗鹤驾鸾骖〖BF〗]〖BFB〗〖HT〗。清〖DK〗·褚人穫《坚瓠集〖DK〗·五集〖DK〗·三〖DK〗·神仙太守》:古今何处有神仙,鹤驾鸾骖总浪传。
又作〖HTH〗〖BF〗[〖BFB〗驾鹤骖鸾〖BF〗]〖BFB〗〖HT〗。张大直《题莲华西洞二首(其二)》(《宋诗纪事续补》二五):真人隐壁君休问,驾鹤骖鸾自有时。
又作〖HTH〗〖BF〗[〖BFB〗控鹤骖鸾〖BF〗]〖BFB〗〖HT〗。王瞻《高盖名山院记》(《全唐文》九○○):是谓控鹤骖鸾之客,以九仙六洞为家;出生离死之人,以大道三界为宅。