昨天面了Zenefits,觉得他家考题蛮有难度,不愧是现在湾区最火startup之一。当时也没写好,这里重写一下。
String s1 = "waeginsapnaabangpisebbasepgnccccapisdnfngaabndlrjngeuiogbbegbuoecccc";
String s2 = "a+b+c-";s2的形式是一个字母加上一个符号,正号代表有两个前面的字符,负号代表有四个,也就是说s2其实是"aabbcccc",不考虑invalid。
在s1中,找出连续或者不连续的s2,也就是说从s1中找出"aa....bb.....cccc",abc顺序不能变,但是之间可以有零个或多个字符,返回共有多少个。在上面这个例子中,有四个。
这种字符串匹配题,不难想到用DP或者recursion,与leetcode的115题结合,就可以做,但有几个地方需要注意:
1. 首先需要将s1和s2转化,s1要把关键字符提取出来,并且做变换,变成"abcabc",s2要把加减号去掉变成"abc",然后再做。
2. 为什么不把这道题直接变成"aabbccccaabbcccc"和"aabbcccc"然后再做呢,为什么要变成单个字符的形式。这道题和LC 115题不一样的地方在于,比如"aaaa"和"aa",在LC的题目中,有6种匹配,而在这道题中,只有三种,因为这道题里必须连续。也就是说如果变成"aabbccccaabbcccc"和"aabbcccc" ,其实只有四种匹配方式,但实际我们用DP或者recursion的时候"aabbccccaabbcccc"和"aabbcccc"也可以匹配,就多算了很多种情况。
3. 所以如果单纯的变成"aabbccccaabbcccc"和"aabbcccc”再做是不行的。变成"abcabc"和"abc"是正道。
4. 如果s1出现“aaabbcccc” 三个a这种情况,怎么办?那么就把s1变成“aabc”,因为"aaa"中可以选前两个a也可以选后两个a,两种匹配方式。
所以感觉这道题的难点还是需要想到转换字符串。
代码如下:
动态规划:
public static int numDistinctDP(String s1, String s2){
<span style="white-space:pre"> </span>StringBuffer sb1 = new StringBuffer();
StringBuffer sb2 = new StringBuffer();
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
//transform s2
for(int i = 0; i < s2.length(); i += 2){
char c = s2.charAt(i);
sb2.append(c);
map.put(c, s2.charAt(i + 1) == '+'? 1: 3);
}
s2 = sb2.toString();
//transform s1
for(int i = 0; i < s1.length() - 1; i++){
char c = s1.charAt(i);
if(map.containsKey(c)){
int value = map.get(c);
if(isValid(s1, i, value, c)){
sb1.append(c);
}
}
}
s1 = sb1.toString();
//start DP
int[][] num = new int[s1.length() + 1][s2.length() + 1];
for(int i = 0; i <= s1.length(); i++){
num[i][0] = 1;
}
for(int i = 1; i <= s1.length(); i++){
for(int j = 1; j <= s2.length(); j++){
num[i][j] += num[i - 1][j];
if(s1.charAt(i - 1) == s2.charAt(j - 1)){
num[i][j] += num[i - 1][j - 1];
}
}
}
return num[s1.length()][s2.length()];
}
<pre name="code" class="java" style="color: rgb(85, 85, 85); font-size: 11px; line-height: 21px;">private static boolean isValid(String s1, int i, int value, char c){
if(i + value > s1.length() - 1) return false;
for(int j = 1; j <= value; j++){
if(s1.charAt(i + j) != c) return false;
}
return true;
}
Recursion写法:转换字符串的方式一样
</pre></p><p style="margin-top: 0px; margin-bottom: 0px; font-size: 11px; font-family: Monaco;"><pre name="code" class="java">public static int numDistinctRecursion(String s1, String s2){
StringBuffer sb1 = new StringBuffer();
StringBuffer sb2 = new StringBuffer();
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
//transform s2
for(int i = 0; i < s2.length(); i += 2){
char c = s2.charAt(i);
sb2.append(c);
map.put(c, s2.charAt(i + 1) == '+'? 1: 3);
}
s2 = sb2.toString();
//transform s1
for(int i = 0; i < s1.length() - 1; i++){
char c = s1.charAt(i);
if(map.containsKey(c)){
int value = map.get(c);
if(isValid(s1, i, value, c)){
sb1.append(c);
}
}
}
s1 = sb1.toString();
return withRecursionHelper(s1, s2);
}
private static boolean isValid(String s1, int i, int value, char c){
if(i + value > s1.length() - 1) return false;
for(int j = 1; j <= value; j++){
if(s1.charAt(i + j) != c) return false;
}
return true;
}
private static int withRecursionHelper(String s1, String s2){
if(s2.length() == 0) return 1;
if(s1.length() == 0) return 0;
if(s1.length() < s2.length()) return 0;
if(s1.charAt(0) != s2.charAt(0)){
return withRecursionHelper(s1.substring(1), s2);
}
return withRecursionHelper(s1.substring(1), s2) + withRecursionHelper(s1.substring(1), s2.substring(1));
}
这道题还有种不需要转换字符串的recursion写法,就是本人在面试中写的,虽然当时写的一团糟啊。。。待补充。。。