【字符串】B044_LC_每個元音包含偶數次的最長子字符串(狀態壓縮)

一、Problem

Given the string s, return the size of the longest substring containing each vowel an even number of times. That is, ‘a’, ‘e’, ‘i’, ‘o’, and ‘u’ must appear an even number of times.

Input: s = "eleetminicoworoep"
Output: 13
Explanation: The longest substring is "leetminicowor" which contains two each of the vowels: 
e, i and o and zero of the vowels: a and u.

二、Solution

方法一:位運算

一開始用雙指針做的,做着做着就做不下去了,看了一眼別人的思路,居然發現是位運算…

  • 用 5 個二進制位表示元音字母出現次數的種類:
    • 位 0 表示元音字母出現偶數次
    • 位 1 表示元音字母出現奇數次

爲什麼這樣做?可以很方便地用異或來表示元音字母的頻次的奇偶性,比如:

00001 ^ 00001 = 00000,前一個狀態表示只有'a'出現奇數次,現在又出現一次'a',兩個狀態異或後狀態爲 00000
00001 ^ 00010 = 00011,上個狀態是 00001,則表示a和e分別出現奇數次了

Q:狀態表示有了,如果記錄遍歷過程中的狀態呢?
A:5 個二進制位一共有 25=322^5 = 32 種狀態可以表示,數組 開 int[32] 即可

算法

  • 定義狀態
    • pos[st]pos[st] 的值 ii 表示子串 s[0:i] 的元音字母頻次狀態爲 stst 時在 ii 位置
  • 思考初始化:
    • pos[0]=0pos[0] = 0 表示元音頻次狀態 0 在位置 0
  • 思考狀態轉移方程
    • 如果狀態 st 沒有出現過,即 pos[st]=1pos[st] = -1,則 pos[st]=i+1pos[st] = i+1,記錄一下當前位置,供下一次使用。
    • 否則該狀態必定經過 >= 2 次,此時可以推斷出當前狀態再次出現,什麼情況下狀態纔會再次出現?肯定是又遇到了遇到過的元音字母啦,故此時可記錄臨時答案與 max 中:max=i+1pos[st]max = i+1-pos[st]
class Solution {
    public int findTheLongestSubstring(String S) {
		char s[] = S.toCharArray(), v[] = new char[] {'a', 'e', 'i', 'o', 'u'};
		int max = 0, st = 0, pos[] = new int[1<<5];
		Arrays.fill(pos, -1);
        pos[0] = 0;
        
		for (int i = 0; i < s.length; i++) {
            for (int k = 0; k < v.length; k++)
            if (s[i] == v[k]) {
                st ^= 1 << k;
                break;
            }
            if (pos[st] == -1)  pos[st] = i + 1;
            else 				max = Math.max(max, i + 1 - pos[st]);
        }
		return max;
    }
}

複雜度分析

  • 時間複雜度:O(n)O(n)
  • 空間複雜度:O(1)O(1)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章