本文已收錄到 AndroidFamily,技術和職場問題,請關注公眾號 [彭旭銳] 和 [BaguTree Pro] 知識星球提問。
T1. 最大字串配對數目(Easy)
T2. 構造最長的新字串(Medium)
T3. 字串連線刪減字母(Medium)
T4. 統計沒有收到請求的伺服器數目(Medium)
https://leetcode.cn/problems/find-maximum-number-of-string-pairs/
題目說明所有字串不相同,因此我們可以列舉每個字串,檢查其反轉是否存在,模板類似於兩數之和;
class Solution {
fun maximumNumberOfStringPairs(words: Array<String>): Int {
val U = 26
val set = HashSet<Int>()
var ret = 0
for (word in words) {
if (word.length != 2) continue
val key = (word[0] - 'a') * U + (word[1] - 'a')
val reversedKey = (word[1] - 'a') * U + (word[0] - 'a')
if (set.contains(reversedKey)) ret ++
set.add(key)
}
return ret
}
}
複雜度分析:
https://leetcode.cn/problems/construct-the-longest-new-string/
根據題意分析,我們總可以將 ABAB..ABAB 置於結果中間,再將 AA 或 BB 置於兩邊。此時所有 AB 都可選,而 AA BB 最多隻能選擇較少者 + 1,分類討論即可:
class Solution {
fun longestString(x: Int, y: Int, z: Int): Int {
return if (x == y) {
(x + y + z) * 2
} else {
(Math.min(x, y) * 2 + 1 + z) * 2
}
}
}
複雜度分析:
https://leetcode.cn/problems/decremental-string-concatenation/
class Solution {
fun minimizeConcatenatedLength(words: Array<String>): Int {
val INF = 0x3F3F3F3F
val n = words.size
val dp = Array(n) { Array(26) { IntArray(26) { INF } }}
// 起始狀態
dp[0][words[0][0] - 'a'][words[0][words[0].length - 1] - 'a'] = words[0].length
for (i in 1 until n) {
val word = words[i]
val x = word[0] - 'a'
val y = word[word.length - 1] - 'a'
// 列舉子問題狀態
for (j in 0 until 26) {
for (k in 0 until 26) {
// 拼接到前方
if (y == j) {
dp[i][x][k] = Math.min(dp[i][x][k], dp[i - 1][j][k] + word.length - 1)
} else {
dp[i][x][k] = Math.min(dp[i][x][k], dp[i - 1][j][k] + word.length)
}
// 拼接到後方
if (x == k) {
dp[i][j][y] = Math.min(dp[i][j][y], dp[i - 1][j][k] + word.length - 1)
} else {
dp[i][j][y] = Math.min(dp[i][j][y], dp[i - 1][j][k] + word.length)
}
}
}
}
var ret = INF
for (j in 0 until 26) {
for (k in 0 until 26) {
ret = Math.min(ret, dp[n - 1][j][k])
}
}
return ret
}
}
複雜度分析:
https://leetcode.cn/problems/count-zero-request-servers/
線性掃描紀錄檔,併線性掃描查詢列表,將紀錄檔記錄投遞到對應的查詢中,同時使用雜湊表對相同伺服器去重。
class Solution {
fun countServers(n: Int, logs: Array<IntArray>, x: Int, queries: IntArray): IntArray {
val m = queries.size
val sets = Array(m) { HashSet<Int>() }
val ret = IntArray(m)
// 暴力
for (log in logs) {
for ((i, query) in queries.withIndex()) {
if (log[1] in query - x .. query) {
sets[i].add(log[0])
}
}
}
// 輸出
for (i in 0 until m) {
ret[i] = n - sets[i].size
}
return ret
}
}
複雜度分析:
需要注意題目中的單調性,對於紀錄檔時間 log_i < log_j,當 log_i 時間晚於 query_k 時,那麼紀錄檔時間更晚的 log_k 必然無法投遞到 query_k 中,而暴力演演算法中沒有利用單調性質。因此,我們先對 log 紀錄檔列表和 queries 查詢列表按時間順序排序,再來使用滑動視窗來維護每個查詢中覆蓋的紀錄檔資訊。
class Solution {
fun countServers(n: Int, logs: Array<IntArray>, x: Int, queries: IntArray): IntArray {
val l = logs.size
val m = queries.size
val ret = IntArray(m)
// 查詢索引
val indexs = Array(m) { it }
// 排序
Arrays.sort(logs) { i1, i2 ->
i1[1] - i2[1]
}
Arrays.sort(indexs) { i1, i2 ->
queries[i1] - queries[i2]
}
// 滑動視窗 + 離散化
var i = 0
var j = 0
val cnts = HashMap<Int, Int>()
for (id in indexs) {
val to = queries[id]
if (to <= x) throw IllegalStateException()
// 拓展右指標
while (j < l && logs[j][1] <= to) {
cnts[logs[j][0]] = cnts.getOrDefault(logs[j][0], 0) + 1
j++
}
// 收縮左指標
while (i < l && logs[i][1] < to - x) {
cnts[logs[i][0]] = cnts[logs[i][0]]!! - 1
if (cnts[logs[i][0]]!! == 0) cnts.remove(logs[i][0])
i++
}
ret[id] = n - cnts.size
}
return ret
}
}
複雜度分析: