题目描述:

给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 'a','e','i','o','u' ,在子字符串中都恰好出现了偶数次。

示例:

示例 1:
输入:s = "eleetminicoworoep"
输出:13
解释:最长子字符串是 "leetminicowor" ,它包含 e,i,o 各 2 个,以及 0 个 a,u 。

示例 2:
输入:s = "leetcodeisgreat"
输出:5
解释:最长子字符串是 "leetc" ,其中包含 2 个 e 。

示例 3:
输入:s = "bcbcbc"
输出:6
解释:这个示例中,字符串 "bcbcbc" 本身就是最长的,因为所有的元音 a,e,i,o,u 都出现了 0 次。

提示:
1 <= s.length <= 5 x 10^5
s 只包含小写英文字母。

解题思路:

状压dp

这道题一看就是状态压缩,于是我首先就写了一发状压dp,但是因为s太长了,这里开的dp是二维的,最后就爆内存了。
其实这种状态压缩的写法并不好,写的时候就感觉很奇怪,之所以奇怪是因为这道题本质上是前缀和,可以用一维的来写,我偏偏写成了二维,最后写出来反正也爆内存了,不过至少这种方法是对的。

const int MAXN= 10000;
class Solution {
public:
    int findTheLongestSubstring(string s) {
        //状压dp?
        int dp[MAXN][MAXN]; //居然可以开这么大 dp[i][j]代表的是从index i到j五个元音的出现情况
        memset(dp,0,sizeof(dp));
        int ans = 0;
        //初始化dp
        int length = s.length();
        for(int i=0;i<length;i++)
        {
            if(s[i] == 'a') dp[i][i] ^= 1;
            if(s[i] == 'e') dp[i][i] ^= 2;
            if(s[i] == 'i') dp[i][i] ^= 4;
            if(s[i] == 'o') dp[i][i] ^= 8;
            if(s[i] == 'u') dp[i][i] ^= 16;
            if(dp[i][i] == 0) ans = 1;
        }
        for(int k=2;k<=length;k++)
        {
            for(int i=k-1;i<length;i++)
            {
                int j=i-k+1;
                dp[j][i] = dp[j][j] ^ dp[j+1][i];
                if(dp[j][i] == 0)
                {
                    ans = k;
                }
            }
        }
        return ans;
    }
};

</br>

前缀和

发现这道题实际上是前缀和后,我立马写了一发前缀和,然后又超时了。

const int MAXN= 500050;
class Solution {
public:
    int findTheLongestSubstring(string s) {
       //前缀和
        int prefix[MAXN];
        memset(prefix,0,sizeof(prefix));
        int ans =0;
        if(s[0] == 'a' ) prefix[0] ^= 1;
        if(s[0] == 'e' ) prefix[0] ^= 2;
        if(s[0] == 'i' ) prefix[0] ^= 4;
        if(s[0] == 'o' ) prefix[0] ^= 8;
        if(s[0] == 'u' ) prefix[0] ^= 16;

       int len = s.length();
       for(int i=1;i<len;i++)
       {
            if(s[i] == 'a' ) prefix[i] = prefix[i-1]^ 1;
            else if(s[i] == 'e' ) prefix[i] = prefix[i-1]^ 2;
            else if(s[i] == 'i' ) prefix[i] = prefix[i-1]^ 4;
            else if(s[i] == 'o' ) prefix[i] = prefix[i-1]^ 8;
            else if(s[i] == 'u' ) prefix[i] = prefix[i-1]^ 16;
            else prefix[i] = prefix[i-1];
       }

    //for(int i=0;i<len;i++) cout<<prefix[i]<<endl;
       for(int k=1;k<=len;k++)
       {
           for(int i=0;i+k-1<len;i++)
           {
                if(i-1 < 0)
                {
                    if( prefix[i+k-1] == 0 ) 
                    {
                        ans = k;
                    }
                }   
                else
                {
                    if( (prefix[i+k-1] ^ prefix[i-1]) == 0 )
                    {
                        ans = k;
                    } 
                }                 
           }
       }
       return ans;
    }
};

</br>

前缀和+哈希优化

当我发现前缀和超时了,我就猜到这道题多半要用到哈希优化了(难道是前缀和基本操作吗)。
当前缀和加上哈希优化,最显著的变化就是不需要再开前缀和数组了,直接拿一个值一直累加(累亦或)下去就行了,以前还碰到一道前缀和+哈希的题,可以看一下。
可以把式子稍微推一下,目标是找到prefrx[i] ^ prefix[j-1] = 0,变换一下可以变成prefix[i] = prefix[j-1]。这样一变就很明显了,把前缀和一直类推下去,然后把出现过的值的最小坐标记录一下,再次碰到时,将两个Index减一下就行了。
需要注意的是需要把records[0]设置为-1,同时还要标记当前的值是否出现过,那就只好把没出现过的值设置为-2了。(我感觉这种做法比较weird,但是至少是对的)。

const int MAXN= 500050;
class Solution {
public:
    int findTheLongestSubstring(string s) {
        //前缀和 + 哈希优化
        int records[100]; //用来记录已经出现的值的位置 records[x]=i;表示前缀和为x的index为i
        for(int i=0;i<100;i++) records[i] = -2; //-2表示没有访问过
        int length =s.length();
        int cur=0;
        int ans=0;
        records[0] = -1;
        for(int i=0;i<length;i++)
        {
            if(s[i] == 'a') cur = cur^1;
            if(s[i] == 'e') cur = cur^2;
            if(s[i] == 'i') cur = cur^4;
            if(s[i] == 'o') cur = cur^8;
            if(s[i] == 'u') cur = cur^16;
            if(records[cur] != -2)
            {
                ans =max(ans,i-records[cur]);
                records[cur] = min(records[cur],i);
            } 
            else
            {
                records[cur] = i;
            }

        }
        return ans;
    }
};

题目链接:

https://leetcode-cn.com/problems/find-the-longest-substring-containing-vowels-in-even-counts/

results matching ""

    No results matching ""