LeetCode 1156 - Swap For Longest Repeated Character Substring


https://leetcode.com/problems/swap-for-longest-repeated-character-substring/
Given a string text, we are allowed to swap two of the characters in the string. Find the length of the longest substring with repeated characters.

Example 1:
Input: text = "ababa"
Output: 3
Explanation: We can swap the first 'b' with the last 'a', or the last 'b' with the first 'a'. Then, the longest repeated character substring is "aaa", which its length is 3.
Example 2:
Input: text = "aaabaaa"
Output: 6
Explanation: Swap 'b' with the last 'a' (or the first 'a'), and we get longest repeated character substring "aaaaaa", which its length is 6.
Example 3:
Input: text = "aaabbaaa"
Output: 4
Example 4:
Input: text = "aaaaa"
Output: 5
Explanation: No need to swap, longest repeated character substring is "aaaaa", length is 5.
Example 5:
Input: text = "abcdef"
Output: 1

Constraints:
  • 1 <= text.length <= 20000
  • text consist of lowercase English characters only

X. https://leetcode.com/problems/swap-for-longest-repeated-character-substring/discuss/355852/Python-Groupby
There are only 2 cases that we need to take care of:
  • extend the group by 1
  • merge 2 adjacent groups together, which are separated by only 1 character

Explanation

For S = "AAABBCB"
[[c, len(list(g))] for c, g in groupby(S)] --> [[A,3],[B,2],[C,1],[B,1]]
collections.Counter(S) --> {A:3, B:3, C:1}
With these two data, calculate the best solution for the two cases above.

Complexity

Time O(N)
Space O(N)
Python:
commented by @henry34
    def maxRepOpt1(self, S):
        # We get the group's key and length first, e.g. 'aaabaaa' -> [[a , 3], [b, 1], [a, 3]
        A = [[c, len(list(g))] for c, g in itertools.groupby(S)]
        # We also generate a count dict for easy look up e.g. 'aaabaaa' -> {a: 6, b: 1}
        count = collections.Counter(S)
        # only extend 1 more, use min here to avoid the case that there's no extra char to extend
        res = max(min(k + 1, count[c]) for c, k in A)
        # merge 2 groups together
        for i in xrange(1, len(A) - 1):
            # if both sides have the same char and are separated by only 1 char
            if A[i - 1][0] == A[i + 1][0] and A[i][1] == 1:
                # min here serves the same purpose
                res = max(res, min(A[i - 1][1] + A[i + 1][1] + 1, count[A[i + 1][0]]))
        return res
https://leetcode.com/problems/swap-for-longest-repeated-character-substring/discuss/355922/C%2B%2B-group-and-count
Collect indexes for each character. Then, count the number of consecutive indices cnt, and track the number of previous consecutive indices cnt1.
Note that we set cnt1 to zero if the gap is larger than one.
In the end, we'll get a max count of the repeated characters with no more than one-character gap. If we have more of that character somewhere in the string (idx[n].size() > mx), we add 1 for the swap operation.
int maxRepOpt1(string text, int res = 1) {
  vector<vector<int>> idx(26);
  for (auto i = 0; i < text.size(); ++i) idx[text[i] - 'a'].push_back(i);
  for (auto n = 0; n < 26; ++n) {
    auto cnt = 1, cnt1 = 0, mx = 0;
    for (auto i = 1; i < idx[n].size(); ++i) {
      if (idx[n][i] == idx[n][i - 1] + 1) ++cnt;
      else {
          cnt1 = idx[n][i] == idx[n][i - 1] + 2 ? cnt : 0;
          cnt = 1;
      }
      mx = max(mx, cnt1 + cnt);        
    }
    res = max(res, mx + (idx[n].size() > mx ? 1 : 0));
  }
  return res;
}

X. https://www.acwing.com/solution/LeetCode/content/3718/
给定一个字符串 text,允许交换字符串中的任意两个字符。找到最长的单字符重复的子串的长度。

(动态规划) O(n)
  1. 我们分别考察每种字符所能得到的最大答案。
  2. 假设我们当前考察的是字符 c。我们通过遍历一次数组的方式,找到单字符就是 c 的情况下的最长单重复子串。
  3. 设 cnt 为字符串中 c 出现的次数;f(i) 表示以 i 结尾且没有替换过不是 c 的字符时的最长但重复子串;g(i) 表示以 i 结尾且替换过不是 c 的字符时的最长但重复子串。
  4. 当 c == text[i] 时,累加 cntf(i)=f(i1)+1g(i)=g(i1)+1,也就是都可以沿着上一次的结果往后累加 1 的长度。
  5. 如果 c != text[i],则 g(i)=f(i1)+1,表示我们强制替换 text[i] 的字符为 c (注意不是交换),所以要从没有替换过的 f 数组转移;f(i)=0,表示如果不替换,那么长度只能归 0。交换只是替换的一种特殊形式,我们都先假设总能交换。如果不能交换,如 aaabaaa 我们无法把中间的 b 用一个新的 a 交换时,可以通过答案不能超过 cnt 来特判。
  6. 答案为 min(cnt,max(f(i),g(i))),也就是所有的 f 和 g 的最大值,但结果不能超过 cnt
  7. 这里的 f 和 g 数组因为之和上一位有关,所以可以化简为变量。

时间复杂度

  • 枚举每种字符时,只需要遍历数组一次,故时间复杂度为 O(n)

空间复杂度

  • 优化后,只需要常数个变量,故空间复杂度为 O(1)
    int solve(char c, const string& text) {
        int n = text.length(), tot = 0;
        int f = 0, g = 0, cnt = 0;
        for (int i = 0; i < n; i++) {
            if (c == text[i]) {
                f++;
                g++;
                cnt++;
            } else {
                g = f + 1;
                f = 0;
            }
            tot = max(tot, max(f, g));
        }

        return min(tot, cnt);
    }

    int maxRepOpt1(string text) {
        int ans = 0;
        for (char c = 'a'; c <= 'z'; c++)
            ans = max(ans, solve(c, text));
        return ans;
    }

https://zxi.mytechroad.com/blog/hashtable/leetcode-1156-swap-for-longest-repeated-character-substring/
Pre-processing
  1. Compute the longest repeated substring starts and ends with text[i].
  2. Count the frequency of each letter.
Main
  1. Loop through each letter
  2. Check the left and right letter
    • if they are the same, len = left + right
      • e.g1. “aa c aaa [b] aaaa” => len = 3 + 4 = 7
    • if they are not the same, len = max(left, right)
      • e.g2. “aa [b] ccc d c” => len = max(2, 3) = 3
  3. If the letter occurs more than len times, we can always find an extra one and swap it with the current letter => ++len
    • e.g.1, count[“a”] = 9 > 7, len = 7 + 1 = 8
    • e.g.2, count[“c”] = 4 > 3, len = 3 + 1 = 4
Time complexity: O(n)
Space complexity: O(n)
X. https://leetcode.com/problems/swap-for-longest-repeated-character-substring/discuss/355866/Intuitive-Java-Solution-With-Explanation
public int maxRepOpt1(String s) {
        int[] freq = new int[26];
        char[] ch = s.toCharArray();
        for(int i=0; i < ch.length; i++)
            freq[ch[i]-'a']++;
        int max = 0;
        for(int i=0; i < ch.length; i++){
            char curr = ch[i];
            int j=i, count = 0, diff = 0;
            while(j < ch.length && (curr == ch[j] || diff == 0) && count < freq[curr-'a']){
                if(curr != ch[j]) 
                 ++diff;
                ++count;
                ++j;
            }
            max = Math.max(max, count);
        }
        for(int i=ch.length-1; i >= 0; i--){
            char curr = ch[i];
            int j=i, count = 0, diff = 0;
            while(j >= 0 && (curr == ch[j] || diff == 0) && count < freq[curr-'a']){
                if(curr != ch[j]) 
                 ++diff;
                ++count;
                --j;
            }
            max = Math.max(max, count);
        }
        return max;
    }
X. https://leetcode.com/problems/swap-for-longest-repeated-character-substring/discuss/357714/Simple-Java-Solution-similar-to-424-Using-Sliding-Window
LeetCode 424
class Solution {
    public int characterReplacement(String s, int k) {
        int len = s.length();
        int[] count = new int[26];
        int start = 0, maxCount = 0, maxLength = 0;
        for (int end = 0; end < len; end++) {
            maxCount = Math.max(maxCount, ++count[s.charAt(end) - 'A']);
            while (end - start + 1 - maxCount > k) {
                count[s.charAt(start) - 'A']--;
                start++;
                maxCount = 0;
                for(int i = 0; i < 26; i++){
                    if(maxCount < count[i]){
                        maxCount = count[i];
                    }
                }
            }
            maxLength = Math.max(maxLength, end - start + 1);
        }
        return maxLength;
    }
}
LeetCode 1156
In this case, just set k=1.
class Solution {
    public int maxRepOpt1(String s) {
        int len = s.length();
        int[] count = new int[26];
        
        int[] newCount = new int[26];
        for(char c:s.toCharArray()) newCount[c-'a']++;
        
        int start = 0, maxCount = 0, maxLength = 0;
        for (int end = 0; end < len; end++) {
            maxCount = Math.max(maxCount, ++count[s.charAt(end) - 'a']);
            while (end - start + 1 - maxCount > 1 || end - start + 1>newCount[s.charAt(start)-'a']) {
                count[s.charAt(start) - 'a']--;
                start++;
                // maxCount = Math.max(count[s.charAt(end)-'a'],count[s.charAt(start)-'a']);
                maxCount=0;
                for(int i = 0; i < 26; i++){
                    if(maxCount < count[i]){
                        maxCount = count[i];
                    }
                }
            }
            maxLength = Math.max(maxLength, end - start + 1);
        }
        return maxLength;
    
    }
}
X. https://leetcode.com/problems/swap-for-longest-repeated-character-substring/discuss/356056/Simple-O(N)-Java-Solution
The idea is to keep track of all repeated character segments and see if the neighbor segments (segments separated by one other character) can be merged:
  1. There exist a third segment with same character, swap a same character from a third segment. Merged length = len(segment1) + 1 + len(segment2)
  2. There does not exist a third segment with same character, swap a character from the first character of the first segment, or swapping the last character from the second segment, to the separating index. Merged length = len(segment1) + len(segment2)
Otherwise, if there are multiple segments of a character but they are not neighbor, we can only swap a character from one to the other. New length = len(segment) + 1
        public int maxRepOpt1(String text) {
        
        Map<Character, List<int[]>> substrs = new HashMap<>();
  
        int l = 0, r = 0, res = 0, n = text.length();
  // Put all character segments in the hashmap
        while(r < n) {
            while(r < n && text.charAt(r) == text.charAt(l)) r++;  
            res = Math.max(res, r - l);
            substrs.computeIfAbsent(text.charAt(l), k -> new ArrayList<>()).add(new int[]{l, r - 1});
            l = r;
        } 
        
        for(char ch : substrs.keySet()) {
            List<int[]> indexes = substrs.get(ch);
            for(int j = 0; j < indexes.size() - 1; j++) {
                int[] ind1 = indexes.get(j), ind2 = indexes.get(j + 1);
                int len1 = ind1[1] - ind1[0] + 1, len2 = ind2[1] - ind2[0] + 1;    
                if(ind1[1] + 1 == ind2[0] - 1)  // neighbor segments
                    res = Math.max(res, (indexes.size() > 2 ? 1 : 0) + len1 + len2);
                else  // not neighbor, swap a char to the longer one
                    res = Math.max(res, Math.max(len1, len2) + 1);
            }
        }
        return res;
    }

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts