一、子字符串出現的位置
題目:
/** * 給出 字符串 text 和 字符串列表 words, 返回所有的索引對 [i, j] 使得在索引對範圍內的子字符串 * text[i]...text[j](包括 i 和 j)屬於字符串列表 words。 * * 示例 1: * 輸入: text = "thestoryofleetcodeandme", words = ["story","fleet","leetcode"] * 輸出: [[3,7],[9,13],[10,17]] * * 示例 2: * 輸入: text = "ababa", words = ["aba","ab"] * 輸出: [[0,1],[0,2],[2,3],[2,4]] * 解釋: * 注意,返回的配對可以有交叉,比如,"aba" 既在 [0,2] 中也在 [2,4] 中 * * 提示: * 所有字符串都只包含小寫字母。 * 保證 words 中的字符串無重複。 * 1 <= text.length <= 100 * 1 <= words.length <= 20 * 1 <= words[i].length <= 50 * 按序返回索引對 [i,j](即,按照索引對的第一個索引進行排序,當第一個索引對相同時按照第二個索引對排序)。 */
說到匹配,很容易就可以聯想到正則表達式,所以我就上手寫了段代碼
public static int[][] method1(String text, String[] words) {
List<List<Integer>> resultList =new ArrayList<>();
List<Integer> integers=new ArrayList<>();
for (String s:words){
Matcher matcher = Pattern.compile(s).matcher(text);
int needAdd=s.length()-1;
while (matcher.find()){
int start=matcher.start();
integers=new ArrayList<>();
integers.add(start);
integers.add(start+needAdd);
resultList.add(integers);
}
}
int[][] result=new int[resultList.size()][2];
int length=result.length;
for (int i=0;i<length;i++){
result[i]=new int[]{resultList.get(i).get(0),resultList.get(i).get(1)};
}
return result;
}
運行結果一看,不行啊,正則表達式不會匹配已經匹配過的位置,只會往後走,但是題目要求交叉匹配啊,也不能不斷設置匹配起點吧,還不如自己寫for循環判斷呢。
我是按照一個一個子字符串去匹配字符串,得出出現位置再加上長度存到嵌套List裏面的,但這樣出來的結果都在數組裏,但是出現的順序和人家對不上,也給我判錯了,那我就再加一個sort()排個序吧,嗯?超時了?
public static int[][] method2(String text, String[] words) {
List<List<Integer>> resultList =new ArrayList<>();
List<Integer> integers=new ArrayList<>();
for (String s:words){
int start=0;
int length=s.length();
while (start+length<text.length()){
while (start+length<text.length()&&text.charAt(start)!=s.charAt(0)){
start++;
continue;
}
if (text.substring(start,start+length).equals(s)){
integers=new ArrayList<>();
integers.add(start);
integers.add(start+length-1);
resultList.add(integers);
start++;
}
}
}
resultList.sort(new Comparator<List<Integer>>() {
@Override
public int compare(List<Integer> o1, List<Integer> o2) {
if (o1.get(0)==o2.get(0)){
if (o1.get(1)>o2.get(1)){
return 1;
}else if (o1.get(1)<o2.get(1)){
return -1;
}else {
return 0;
}
}else if (o1.get(0)>o2.get(0)){
return 1;
}else {
return -1;
}
}
});
int[][] result=new int[resultList.size()][2];
int length=result.length;
for (int i=0;i<length;i++){
result[i]=new int[]{resultList.get(i).get(0),resultList.get(i).get(1)};
}
return result;
}
哦,懂了題目的意思了,我要去遍歷字符串,對每個位置進行子字符串的匹配,匹配不成功就下一個,這樣效率更高一些,但是最後還是需要一個排序。我不知道這是不是最佳解法,這是一道比賽的題,沒有答案,只能看到提交通過沒通過。如果你有更好的解決方式歡迎和我討論。
public static int[][] method3(String text, String[] words) {
List<List<Integer>> resultList =new ArrayList<>();
List<Integer> integers=new ArrayList<>();
int start=0;
while (start<text.length()){
for (String s:words){
int length=s.length();
if (start+length>text.length()){
continue;
}
int i=0;
for (;i<length;i++){
if (text.charAt(start+i)!=s.charAt(i)){
break;
}
}
if (i==length){
integers=new ArrayList<>();
integers.add(start);
integers.add(start+length-1);
resultList.add(integers);
}
}
start++;
}
resultList.sort(new Comparator<List<Integer>>() {
@Override
public int compare(List<Integer> o1, List<Integer> o2) {
if (o1.get(0)==o2.get(0)){
if (o1.get(1)>o2.get(1)){
return 1;
}else if (o1.get(1)<o2.get(1)){
return -1;
}else {
return 0;
}
}else if (o1.get(0)>o2.get(0)){
return 1;
}else {
return -1;
}
}
});
int[][] result=new int[resultList.size()][2];
int length=result.length;
for (int i=0;i<length;i++){
result[i]=new int[]{resultList.get(i).get(0),resultList.get(i).get(1)};
}
return result;
}
二、最長公共字符串
題目:
/** * 對於字符串 S 和 T,只有在 S = T + ... + T(T 與自身連接 1 次或多次)時,我們才認定 “T 能除盡 S”。 * 返回字符串 X,要求滿足 X 能除盡 str1 且 X 能除盡 str2。 * * 示例 1: * 輸入:str1 = "ABCABC", str2 = "ABC" * 輸出:"ABC" * * 示例 2: * 輸入:str1 = "ABABAB", str2 = "ABAB" * 輸出:"AB" * * 示例 3: * 輸入:str1 = "LEET", str2 = "CODE" * 輸出:"" * * 提示: * 1 <= str1.length <= 1000 * 1 <= str2.length <= 1000 * str1[i] 和 str2[i] 爲大寫英文字母 */
找兩個字符串的最長公共子串,如果你去一個字符一個字符增加,這樣去挨個匹配的話肯定是不行的,因爲字符串有可能很長。你可以從子字符串構成入手,加入set,看看組成最小的子字符串需要多少個字符,這樣截取出來子字符串去匹配,如果可以匹配就加長一倍再進行匹配,如此往復,直到最後無法匹配,得出最長的公共字符串。
還要一種思路就是從字符串長度入手,因爲要求最長公共子字符串,所以長度肯定也是兩個字符串的公因子,求出兩個字符的最大公因子,首先按照最短的字符串來匹配,不斷地去減去最大公因子的長度,如果能匹配到就返回當前的長度。
匹配可以使用String.split()函數,該函數返回按照傳入分割符分割後組成的數組,如果該字符創都是由分隔符組成的,那麼返回的數組應該爲0
我用的是第二種思路,比賽的時候寫的比較急。
public static String gcdOfStrings(String str1, String str2) {
if (str2.length()>str1.length()){
String temp=str1;
str1=str2;
str2=temp;
}
int step=GCD(str1.length(),str2.length());
int end=str2.length();
while (end>0){
String fenge=str2.substring(0,end);
if (str1.split(fenge).length==0&&str2.split(fenge).length==0){
return fenge;
}
end-=step;
}
return "";
}
public static int GCD(int a,int b) {
if(b==0)
return a;
else
return GCD(b,a%b);
}
三、翻轉矩陣
題目:
/** * 給定由若干 0 和 1 組成的矩陣 matrix,從中選出任意數量的列並翻轉其上的 每個 單元格。 * 翻轉後,單元格的值從 0 變成 1,或者從 1 變爲 0 。 * 返回經過一些翻轉後,行上所有值都相等的最大行數。 * * 示例 1: * 輸入:[[0,1],[1,1]] * 輸出:1 * 解釋:不進行翻轉,有 1 行所有值都相等。 * * 示例 2: * 輸入:[[0,1],[1,0]] * 輸出:2 * 解釋:翻轉第一列的值之後,這兩行都由相等的值組成。 * * 示例 3: * 輸入:[[0,0,0],[0,0,1],[1,1,0]] * 輸出:2 * 解釋:翻轉前兩列的值之後,後兩行由相等的值組成。 * * 提示: * 1 <= matrix.length <= 300 * 1 <= matrix[i].length <= 300 * 所有 matrix[i].length 都相等 * matrix[i][j] 爲 0 或 1 */
題目需要好好讀一讀,知道是怎麼翻轉的。因爲翻轉是按照列進行翻轉的,結果是看行數字是否都一樣,所以說翻轉一列所有行都會受到影響,就不存在複雜的行與行配合的情況,核心就在兩種情況上,一種就是原本就是全1或者全0的行,另一種就是兩行正好互補的情況,你0我1,你1我0,這樣翻轉之後纔可以變成全0或者全1。
明確了核心這題就很好解了,因爲給的矩陣順序是亂的,所以你要先對其進行排序,排序很簡單就不說了。然後先把全0和全1的行的數量統計出來,然後因爲矩陣已經排好序了,所以你就可以用頭尾指針不斷比較是否可以匹配
如果兩行加和數字中存在2,那就說明有兩個相同的位置上都是1,說明尾指針大了,尾指針前移;
如果兩行加和的數字存在0,那就說明有兩個相同的位置上都是0,說明頭指針小了,頭指針前移;
我在其中加入了startnum和endnum,是因爲有可能幾行的數字都是完全一樣的,沒必要重複判斷,減少判斷次數。
在定義List的時候我還加入了一個0,是因爲可能沒有一行可以變成全1或全0的,防止報錯。
public static int maxEqualRowsAfterFlips(int[][] matrix) {
sort(matrix,0,matrix.length-1);
List<Integer> integerList=new ArrayList<>();
integerList.add(0);
int start=0,end=matrix.length-1,startnum=0,endnum=0;
if (matrix[start]==matrix[end]){
return matrix.length;
}
while (true){
int i=0;
for (;i<matrix[start].length;i++){
if (matrix[start][i]!=0){
break;
}
}
if (i==matrix[start].length){
start++;
startnum++;
}else {
break;
}
}
integerList.add(startnum);
while (true){
int i=0;
for (;i<matrix[end].length;i++){
if (matrix[end][i]!=1){
break;
}
}
if (i==matrix[end].length){
end--;
endnum++;
}else {
break;
}
}
integerList.add(endnum);
while (start<=end){
startnum=1;
endnum=1;
while (start<end&&matrix[start]==matrix[start+1]){
startnum++;
start++;
}
while (start<end&&matrix[end]==matrix[end-1]){
endnum++;
end--;
}
int i=0;
for (;i<matrix[start].length;i++){
if (matrix[start][i]+matrix[end][i]!=1){
break;
}
}
if (i==matrix[start].length){
integerList.add(startnum+endnum);
start++;
end--;
}else {
if (matrix[start][i]+matrix[end][i]==2){
end--;
}else {
start++;
}
}
}
integerList.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1==o2?0:o1>o2?-1:1;
}
});
return integerList.get(0);
}
public static void sort(int[][] array,int left,int right){
if (left < right) {
int i = left, j = right;
int[] pivot = array[left];
while (i < j) {
while (i < j && compare(array[j],pivot)>=0) {
j--;
}
if (i < j) {
array[i] = array[j];
i++;
}
while (i < j && compare(array[j],pivot)==-1) {
i++;
}
if (i < j) {
array[j] = array[i];
j--;
}
}
array[i] = pivot;
sort(array, left, i - 1);
sort(array, i + 1, right);
}
}
public static int compare(int[] A,int[] B){
int length=A.length;
for (int i=0;i<length;i++){
if (A[i]>B[i]){
return 1;
}else if (A[i]<B[i]){
return -1;
}
}
return 0;
}