一、子字符串出现的位置
题目:
/**
* 给出 字符串 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;
}