問題:
難易度:中
説明:
文字列、数値K、および文字内の文字列を任意の順序で指定すると、すべての文字の中で最も長い文字の長さが繰り返しになります> = K部分文字列(連続シーケンス)が返されます。
件名リンク:https://leetcode.com/problems/longest-substring-with-at-least-k-repeating-characters/
入力範囲:
1 <= s.length <= 104
s
小文字の英字のみで構成されます。1 <= k <= 105
ケースを入力してください:
Example 1:
Input: s = "aaabb", k = 3
Output: 3
Explanation: The longest substring is "aaa", as 'a' is repeated 3 times.
Example 2:
Input: s = "ababbc", k = 2
Output: 5
Explanation: The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times.
私のコード:
長い間考えていたのですが、これも分割統治法に似ていますが、これ以上便利な考え方はなく、もっと体系的に対処しなければならなかったので、初版は非常に長く、最適化後にブレークポイントがあるはずです。
1.文字列全体の単語頻度を1回計算します
2.次に、単語頻度がkより低い文字の単語頻度を0に設定します。
3.単語頻度が0に設定されているため、間隔を0単語頻度で割った値が表示され、間隔ごとに単語頻度が再計算され、2ステップ続行されます。
4.最後に、各間隔の長さを数えます
class Solution {
public int longestSubstring(String s, int k) {
if(k < 2) return s.length();
int len = s.length(), total = 0, temp = 0;
int[] fre = new int[len];
int[] totals = new int[26];
char[] chs = s.toCharArray();
for(char ch : chs) // 先算一次词频
totals[ch - 'a'] ++;
for(int i = 0;i < len;i ++) // 对整个串都赋值上词频
fre[i] = totals[chs[i] - 'a'];
boolean zeroF = false;
while(true) {
for(int i = 0;i < len;i ++)
if(fre[i] != 0 && fre[i] < k) { // 将低于 k 除 0 外的词频置为0
zeroF = true;
fre[i] = 0;
}
if(!zeroF) break;
for(int i = 0;i < len;i ++) { // 把每个 0 对应的区间都进行重复计算
if(fre[i] == 0) {
reclculate(i + 1, len, fre, chs);
} else if(i == 0) recalculate(0, len, fre, chs);
}
zeroF = false;
}
for(int i = 0;i < len;i ++) { // 最后获得最长的区间长度
if(fre[i] == 0) {
total = Math.max(temp, total);
temp = 0;
} else
temp ++;
}
return Math.max(temp, total);
}
// 重新计算每个区间的重复次数
private void recalculate(int i, int len, int[] fre, char[] chs) {
if(i < len && fre[i] != 0) {
int[] totals = new int[26];
for(int j = i;j < len;j ++) {
if(fre[j] == 0) {
len = j;
break;
}
totals[chs[j] - 'a'] ++;
}
for(int j = i;j < len;j ++) {
fre[j] = totals[chs[j] - 'a'];
}
}
}
}