• [LeetCode] 340. Longest Substring with At Most K Distinct Characters


    Given a string, find the length of the longest substring T that contains at most k distinct characters.

    Example 1:

    Input: s = "eceba", k = 2
    Output: 3
    Explanation: T is "ece" which its length is 3.

    Example 2:

    Input: s = "aa", k = 1
    Output: 2
    Explanation: T is "aa" which its length is 2.


    Solution 1. Brute force, O(N^3) runtime

    For each possible substring, check if it contains at most k distinct characters, then get the longest.

     1 class Solution {
     2     public int lengthOfLongestSubstringKDistinct(String s, int k) {
     3         int max = 0;
     4         for(int i = 0; i < s.length(); i++) {
     5             for(int j = i + 1; j <= s.length(); j++) {
     6                 String sub = s.substring(i, j);
     7                 Set<Character> chars = new HashSet<>();
     8                 for(int t = i; t < j; t++) {
     9                     chars.add(s.charAt(t));
    10                 }
    11                 if(chars.size() <= k) {
    12                     max = Math.max(max, sub.length());
    13                 }
    14             }
    15         }
    16         return max;
    17 }

    Solution 2. O(N^2) runtime

    One thing that can be optimized in solution 1 is to use checked substring's characters counts to determine if the next substring of the same length satisfies the condition, from O(N) to O(1). This is done by maintaining a hashmap of each distinct character's frequency. 

    1. Initialize a hash map of each distinct character's frequency.

    2. Starting from the possible maximum length n = s.length(), do the following.

      a. from left to right, slide a window of length n to check if a substring meets the required condition. If it does, return; otherwise keep sliding one character at a time until reaching the right end.

      b. reduce sliding window length by 1 and from right to left, slide a window of length n to check if a substring meets the required condition. If it does, return; otherwise keep sliding one character at a time until reaching the left end.

      c. repeat a and b until n = 0.

     1 class Solution {
     2     public int lengthOfLongestSubstringKDistinct(String s, int k) {        
     3         int[] counts = new int[256];
     4         for(int i = 0; i < s.length(); i++) {
     5             counts[s.charAt(i) - '']++;
     6         }
     7         int uniqueChars = 0;
     8         for(int i = 0; i < 256; i++) {
     9             if(counts[i] > 0) {
    10                 uniqueChars++;
    11             }
    12         }
    13         int maxLen = s.length();
    14         boolean leftToRight = false;
    15          
    16         for(; maxLen > 0; maxLen--) {
    17             leftToRight = !leftToRight;
    18             if(leftToRight) {
    19                 int leftIdx = 0, rightIdx = maxLen - 1;
    20                 if(rightIdx + 1 < s.length()) {
    21                     counts[s.charAt(rightIdx + 1) - '']--;
    22                     if(counts[s.charAt(rightIdx + 1) - ''] == 0) {
    23                        uniqueChars--; 
    24                     }
    25                 }
    26                 if(uniqueChars <= k) {
    27                     return maxLen;
    28                 }
    29                 rightIdx ++;
    30                 while(rightIdx < s.length()) {  
    31                     counts[s.charAt(leftIdx) - '']--;
    32                     if(counts[s.charAt(leftIdx) - ''] == 0) {
    33                         uniqueChars--;
    34                     }
    35                     leftIdx++;
    36                     
    37                     if(counts[s.charAt(rightIdx) - ''] == 0) {
    38                         uniqueChars++;
    39                     }
    40                     
    41                     counts[s.charAt(rightIdx) - '']++;                  
    42                     if(uniqueChars <= k) {
    43                         return maxLen;
    44                     }
    45                     rightIdx++;
    46                 }               
    47             }
    48             else {
    49                 int rightIdx = s.length() - 1, leftIdx = s.length() - maxLen;
    50                 if(leftIdx >= 1) {
    51                     counts[s.charAt(leftIdx - 1) - '']--;
    52                     if(counts[s.charAt(leftIdx - 1) - ''] == 0) {
    53                        uniqueChars--; 
    54                     }                    
    55                 }
    56                 if(uniqueChars <= k) {
    57                     return maxLen;
    58                 }
    59                 leftIdx--;
    60                 while(leftIdx >= 0) {  
    61                     counts[s.charAt(rightIdx) - '']--;
    62                     if(counts[s.charAt(rightIdx) - ''] == 0) {
    63                         uniqueChars--;
    64                     }
    65                     rightIdx--;
    66                     
    67                     if(counts[s.charAt(leftIdx) - ''] == 0) {
    68                         uniqueChars++;
    69                     }
    70                     counts[s.charAt(leftIdx) - '']++;                    
    71                     if(uniqueChars <= k) {
    72                         return maxLen;
    73                     }
    74                     leftIdx--;
    75                 }               
    76             }
    77         }
    78         return maxLen;   
    79 }

    Solution 3. O(N) runtime

    To further optimize solution 2, we have the this observation: for a substring that starts at index i, s[i, j - 1], if it meets the condition while s[i, j] does not, we do not need to backtrack j to i + 1.This is true because all the substring from index i + 1 to j - 1 are a smaller set of s[i, j - 1]. If s[i, j - 1] is a possible solution, it eliminates the need of checking a smaller solution. We just need to increment i by 1 and pick up where j stopped. 

    For substrings that start at index i, there are two cases when we will stop incrementing j.

    1. s[i, j] has more than k distinct characters; In this case, we need to update the frequency of s.charAt(i), increment i by 1 then repeat the same process.

    2. j is out of bound, j == s.length();  In this case, we've found the answer as no other qualified substrings will have a longer length(i can only be incremented).

    The runtime is O(N) as both the start index i and end index j only move forward, which means at any given iteration, either i or j is incremented. This takes linear time to finish.

     1 class Solution {
     2     public int lengthOfLongestSubstringKDistinct(String s, int k) {       
     3         if(s == null || s.length() == 0 || k <= 0){
     4             return 0;
     5         }        
     6         int[] counts = new int[256];
     7         int distinctChars = 0;
     8         int endIdx = 0, maxLen = 0;
     9         for(int startIdx = 0; startIdx < s.length(); startIdx++) {
    10             while(endIdx < s.length()) {
    11                 if(counts[s.charAt(endIdx) - ''] > 0) {
    12                     counts[s.charAt(endIdx) - '']++;
    13                 }
    14                 else if(distinctChars == k){
    15                     break;
    16                 }
    17                 else {
    18                     counts[s.charAt(endIdx) - '']++;
    19                     distinctChars++;
    20                 }
    21                 endIdx++;
    22             }
    23             maxLen = Math.max(maxLen, endIdx - startIdx);
    24             if(endIdx == s.length()) {
    25                 break;
    26             }
    27             counts[s.charAt(startIdx) - '']--;
    28             if(counts[s.charAt(startIdx) - ''] == 0) {
    29                 distinctChars--;
    30             }
    31         }
    32         return maxLen;
    33     }
    34 }






  • 相关阅读:
    DOM编程
    BOM编程
    JavaScript
    CSS
    HTML入门
    shiro与项目集成开发
    shiro授权测试
    散列算法
    shiro认证流程
    spring boot 入门及示例
  • 原文地址:https://www.cnblogs.com/lz87/p/10095363.html
Copyright © 2020-2023  润新知