力扣面试题 16.18. 模式匹配
你有两个字符串,即pattern和value。
pattern字符串由字母"a"和"b"组成,用于描述字符串中的模式。例如,字符串"catcatgocatgo"匹配模式"aabab"(其中"cat"是"a",“go"是"b”),该字符串也匹配像"a"、"ab"和"b"这样的模式。
但需注意"a"和"b"不能同时表示相同的字符串。编写一个方法判断value字符串是否匹配pattern字符串。
示例 1:
输入: pattern = "abba", value = "dogcatcatdog"
输出: true
示例 2:
输入: pattern = "abba", value = "dogcatcatfish"
输出: false
示例 3:
输入: pattern = "aaaa", value = "dogcatcatdog"
输出: false
示例 4:
输入: pattern = "abba", value = "dogdogdogdog"
输出: true
解释: "a"="dogdog",b="",反之也符合规则
提示:
- 0 <= len(pattern) <= 1000
- 0 <= len(value) <= 1000
- 你可以假设pattern只包含字母"a"和"b",value仅包含小写字母。
分析
主方法里就是遍历数字解方程,
x*countA+y*countB = valueLen
找到可行解后就可以根据解来判断是否能匹配。
当然,在解方程之前先把特殊情况解决掉,比如字符串为空、a或者b代表空字符串的情况。排除掉这几种特殊情况后,解方程里a和b的数量肯定是都超过1了。
思路全在注释里了。
代码
class Solution {
public boolean patternMatching(String pattern, String value) {
int plen = pattern.length();
int vlen = value.length();
//两个字符串都是空,能匹配。
if(vlen == 0 && plen == 0) return true;
//模式串不为空,需要判断模式串是不是全是一个字母。
if(vlen == 0 && plen != 0) {
for(int i = 1; i<plen; i++){
if(pattern.charAt(i) != pattern.charAt(i-1)) return false;
}
return true;
};
//模式串为空,无法匹配。
if(plen == 0) return false;
//计算a和b的数量
int countA = 0;
int countB = 0;
for(char c:pattern.toCharArray()){
if(c == 'a') countA++;
else countB++;
}
//判断a或者b为0的情况
if(countA == 0){
if(check(value,countB)) return true;
else return false;
}
if(countB == 0){
if(check(value,countA)) return true;
else return false;
}
//先测试能否单独被a或者b分割
if(check(value, countA) || check(value, countB)) return true;
//现在不能被单个字符分割
//x是a代表的字符串的长度,y是b代表的字符串的长度
int x = 0, y = 0;
//x*countA+y*countB = vLen
for(y = 1; y<vlen; y++){
if((vlen-countB*y)%countA == 0 && ((vlen-countB*y)/countA)>0){
//能整除,说明找到了一个解,验证这个解对不对。
x = (vlen-countB*y)/countA;
//System.out.println(x+" "+y);
if(check(value, pattern, countA, countB, x, y)) return true;
}
}
//没有可行解,返回匹配失败。
return false;
}
public boolean check(String value, int a){
//判断能否用单个字母分割
//如果不能整除,匹配失败。
if(value.length()%a!=0) return false;
int fenge = value.length()/a;
//首个子串
String subvalue = value.substring(0,fenge);
for(int i = 0; i<a;i++){
int left = i*fenge;
int right = (i+1)*fenge;
//判断后面的子串是否和第一个相同。
if(!value.substring(left, right).equals(subvalue)) return false;
}
return true;
}
public boolean check(String value, String pattern, int a, int b, int x, int y){
//判断两个字母均需要匹配的情况。
//找到a与b在模式串中的位置
ArrayList<Integer> listA = new ArrayList<>();
ArrayList<Integer> listB = new ArrayList<>();
char[] p = pattern.toCharArray();
for(int i = 0; i<p.length; i++){
if(p[i] == 'a') listA.add(i);
else listB.add(i);
}
//计算a和b的起点。
int aStart = listA.get(0)*y;
int bStart = listB.get(0)*x;
//取出a和b分别代表的子串。
String subA = value.substring(aStart,aStart+x);
String subB = value.substring(bStart,bStart+y);
//a和b如果相同,匹配失败。
if(subA.equals(subB)) return false;
//开始匹配a
//判断后面的子串是不是和起始子串一样
for(int i = 1; i<listA.size(); i++){
if(listA.get(i)!=listA.get(i-1)+1){
//如果两个a不是连续的,说明中间夹了n个B
aStart = aStart+x+y*(listA.get(i)-listA.get(i-1)-1);
String tmp = value.substring(aStart,aStart+x);
if(!tmp.equals(subA)) return false;
}else{
//两个a连续,可以直接取子串。
aStart += x;
String tmp = value.substring(aStart,aStart+x);
if(!tmp.equals(subA)) return false;
}
}
//判断b,和a同理,不再写注释。
for(int i = 1; i<listB.size(); i++){
if(listB.get(i)!=listB.get(i-1)+1){
bStart = bStart+y+x*(listB.get(i)-listB.get(i-1)-1);
String tmp = value.substring(bStart,bStart+y);
if(!tmp.equals(subB)) return false;
}else{
bStart += y;
String tmp = value.substring(bStart,bStart+y);
if(!tmp.equals(subB)) return false;
}
}
//此解匹配成功。
return true;
}
}