刷题日记 - 4
记录刷题。
难度:中等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class Solution { public long numberOfRightTriangles(int[][] grid) { int m = grid.length; int n = grid[0].length; int[] rowPoints = new int[m]; int[] colPoints = new int[n];
for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (grid[i][j] == 1) { rowPoints[i]++; colPoints[j]++; } } }
long res = 0;
for (int i = 0; i < m; i++) { if (rowPoints[i] < 2) continue; for (int j = 0; j < n; j++) { if (grid[i][j] == 1) { res += (colPoints[j] - 1) * (rowPoints[i] - 1); } } }
return res; } }
|
如果某个位置有一个点(即 grid[i][j] == 1
),则以该点为直角计算直角三角形的数量。
难度:简单
KMP算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| class Solution { public int strStr(String haystack, String needle) { return kmp(haystack,needle); }
private int[] computeLSPArray(String pattern){ int len = pattern.length(); char[] charArray = pattern.toCharArray(); int[] lsp = new int[len]; lsp[0] = 0;
int i = 1; int j = 0;
while(i<len){ if(charArray[i]==charArray[j]){ j++; lsp[i] = j; i++; }else{ if(j!=0){ j = lsp[j-1]; }else{ i++; } } } return lsp; }
private int kmp(String text,String pattern){
int N = text.length(); int M = pattern.length();
char[] textChars = text.toCharArray(); char[] patternChars = pattern.toCharArray();
int[] lsp = computeLSPArray(pattern);
int i = 0; int j = 0;
while(i<N){ if(textChars[i]==patternChars[j]){ i++; j++; }
if(j==M){ int idx = i-j; return idx;
}else if(i<N && textChars[i]!=patternChars[j]){ if(j==0){ i++; }else{ j = lsp[j-1]; } } } return -1; } }
|
主要是KMP,因为已经单独列出一个博客了,所以这里就没加注释。
难度:困难
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| class Solution { public List<String> fullJustify(String[] words, int maxWidth) { List<String> res = new ArrayList<>(); LinkedList<String> tmp = new LinkedList<>(); int tmpLen = 0; int SPACE = 1; int len = words.length; int p = 0; while(p < len) { String word = words[p]; if(tmpLen == 0) { tmpLen += word.length(); tmp.offer(word); } else if(tmpLen + word.length() + SPACE <= maxWidth) { tmpLen = tmpLen + word.length() + SPACE; tmp.offer(word); } else { String tmpString = fun(tmp, tmpLen, maxWidth); res.add(tmpString); tmpLen = 0; continue; } p++; }
StringBuilder sb = new StringBuilder(); sb.append(tmp.poll()); while(!tmp.isEmpty()){ sb.append(' '); sb.append(tmp.poll()); } if(sb.length()<maxWidth){ for(int i=sb.length();i<maxWidth;i++){ sb.append(' '); } } res.add(sb.toString()); return res; } private String fun(LinkedList<String> tmp, int tmpLen, int maxWidth) { int wordCount = tmp.size(); int spaceCount = maxWidth - tmpLen + wordCount - 1; StringBuilder sb = new StringBuilder(); int p = 0; int[] spaces = new int[wordCount - 1]; if(wordCount > 1) { int base = spaceCount / (wordCount - 1); for(int i = 0; i < wordCount - 1; i++) { spaces[i] = base; } int remain = spaceCount % (wordCount - 1); int idx = 0; while(remain > 0) { spaces[idx]++; remain--; idx++; } } while(p < wordCount) { String word = tmp.poll(); if(p == wordCount - 1) { sb.append(word); } else { sb.append(word); for(int i = 0; i < spaces[p]; i++) { sb.append(' '); } } p++; }
if(sb.length() < maxWidth) { for(int i = sb.length(); i < maxWidth; i++) { sb.append(' '); } } return sb.toString(); } }
|
主要难点在于处理文本对齐过程中涉及的多个细节:
- 控制行宽度
需要准确控制每行的宽度不超过给定的 maxWidth
。这包括计算当前行的单词长度和空格长度,以及判断何时需要换行。
- 处理不同的对齐方式
对于每行的对齐方式,需要考虑以下几种情况:
- 普通行:除了最后一行外的所有行需要两端对齐,空格要尽可能均匀分布。
- 最后一行:最后一行只需左对齐,且单词之间只需一个空格,剩余空格全部放在行尾。
- 分配空格
在两端对齐时,必须均匀分配空格,并处理空格无法均匀分配的情况:
- 均匀分配:当空格数能够均匀分配时,每个间隔的空格数相同。
- 不均匀分配:当空格数不能均匀分配时,需优先在前面的间隔中多加一个空格。
难度:简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Solution { public boolean isPalindrome(String s) { char[] charArray = s.toCharArray();
StringBuilder sb = new StringBuilder(); for (char ch : charArray) { if ('A' <= ch && ch <= 'Z') { sb.append((char) (ch - 'A' + 'a')); } else if (('a' <= ch && ch <= 'z') || ('0' <= ch && ch <= '9')) { sb.append(ch); } }
char[] newCharArray = sb.toString().toCharArray(); int head = 0; int tail = sb.length() - 1; boolean flag = true;
while (head < tail) { char headChar = newCharArray[head]; char tailChar = newCharArray[tail]; if (headChar != tailChar) { flag = false; break; } head++; tail--; }
return flag; } }
|
这个题比较简单,只需要注意下面两点:
难度:简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class Solution { public boolean isSubsequence(String s, String t) { boolean res = false; int sLen = s.length(); int tLen = t.length(); if (sLen < 1) { return true; }
char[] sChars = s.toCharArray(); char[] tChars = t.toCharArray();
int q = 0; int p = 0;
char sCur = sChars[q]; char tCur; while (p < tLen) { tCur = tChars[p]; if (tCur == sCur) { q++; if (q == sLen) { res = true; break; } sCur = sChars[q]; } p++; }
return res; } }
|
当给出的输入只有一个s
的时候,还是挺简单的,只需要注意一下几个方面:
难度:中等
上一题的进阶,但是现在给出的不仅是一个字符串,给出的是字符串数组,求满足文本串子序列的字符串的个数。
遍历字符串数组中的所有字符串使用上一题的方法进行判断是否是子序列,这样的做法会超时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| class Solution { public int numMatchingSubseq(String s, String[] words) { char[] sChars = s.toCharArray(); int len = s.length(); int[][] dp = new int[len+1][26]; for (char ch = 'a'; ch <= 'z'; ch++) { int nextPos = -1; for (int i = len - 1; i >= 0; i--) { if (sChars[i] == ch) { nextPos = i; } dp[i][ch - 'a'] = nextPos; } }
Arrays.fill(dp[len], -1);
int res = 0; boolean flag; for (String word : words) { flag = true; int idx = -1; for (char ch : word.toCharArray()) { idx = dp[idx + 1][ch - 'a']; if (idx == -1) { flag = false; break; } } if (flag) res++; }
return res; } }
|
用朴素的思路为什么会超时:
朴素的判定过程需要使用双指针扫描两个字符串,其中对于原串的扫描,会有大量的字符会被跳过(无效匹配),即只有两指针对应的字符相同时,匹配串指针才会后移。
如果给出的短串数量很多,就会出现很多次无效匹配。
预处理原串之后有什么好处:
对于要匹配的短字符串,遍历每一个字符,不断地寻找该字符在长字符串中的位置,然后将位置更新,寻找下一个字符,相当于在长字符串上“跳跃”。
如果下一个位置为 -1
,表示长字符串再没有该字符了,表明短字符串不是长字符串的子序列;如果能正常遍历完毕,则表明是子序列。
这样只需要预处理一次原串。