Leetcode5485. 找出最长的超赞子字符串【第 32 场双周赛】

题目链接
题意:给你一个字符串 S S S。请返回 S S S 中最长的 超赞子字符串 的长度。
「超赞子字符串」需满足满足下述两个条件:

  • 该字符串是 S S S 的一个非空子字符串
  • 进行任意次数的字符交换重新排序后,该字符串可以变成一个回文字符串

1 ≤ s . l e n g t h ≤ 1 0 5 1\le s.length \le 10^5 1s.length105 s s s 仅由数字组成

思路:我们首先明确「超赞子字符串」的性质,因为可以任意交换顺序,所以它仅需满足回文串中字符数量的限制,而无需满足顺序限制;所以字符串是「超赞子字符串」的充要条件是至多有一个字符出现奇数次,又考虑到 S S S仅由数字组成,我们需要考虑的就仅仅是 ′ 0 ′ − ′ 9 ′ '0'-'9' 09 10 10 10个字符的奇偶性,容易想到状压dp。我们使用10位二进制储存每一个数字出现次数的奇偶,令 d p [ i ] dp[i] dp[i]记录 s [ 0 ] − s [ i ] s[0]-s[i] s[0]s[i]每一个数字出现的奇偶性,1表示出现次数为奇数,0表示出现次数为偶数,例如 S = " 1231 " S="1231" S="1231",则 d p [ 4 ] = 0000001100 dp[4]=0000001100 dp[4]=0000001100(从低到高位表示’0’-‘9’),转移方程是 d p [ i ] = d p [ i − 1 ] ^ ( 1 < < ( s [ i ] − ′ 0 ′ ) ) dp[i]=dp[i-1] \text{\textasciicircum} (1<<(s[i]-'0')) dp[i]=dp[i1]^(1<<(s[i]0))对于每一个状态,我们存储它第一次出现的下标,对于每一次循环,我们分为
1.子字符串为全部偶数(则该状态已经出现过,因为某一位经过偶数次异或会回归原状态)。
2.子字符串有一个字符出现次数为奇数(则我们分别对10个位进行反转,将这个出现次数为奇数的位反转的时候会变成偶数的情况,和情况1进行相同的的判断)
注意:注意出现次数全部为偶数的情况,如果这个子字符串不是和原串相等,就可以在左或右侧添加一个字符进来,仍然为「超赞子字符串」。
AC代码:

class Solution {
public:
    int longestAwesome(string s) {
        int n=s.size(),ans=1;
        vector<int> dp(n+1),cnt(1025,n+1);//至多2^10种状态
        for(int i=0;i<=n;i++){
            if(i) dp[i]=dp[i-1]^(1<<(s[i-1]-'0'));//处理前缀异或和
            if(cnt[dp[i]]==n+1) cnt[dp[i]]=i;
            else ans=max(ans,min(n,i-cnt[dp[i]]+1));//最长偶数段,显然可以在两侧有额外字符时+1
            //最长奇数段,把每一位都切换奇偶性,即在这一段中删除此位,查询之前是否出现过
            //然后和偶数段一样的判断,仍然是因为偶数段+1个其它数字也是超赞字符串
            for(int j=0;j<10;j++)
                if(cnt[dp[i]^(1<<j)]!=n+1)
                    ans=max(ans,min(n,i-cnt[dp[i]^(1<<j)]));
        }
        return ans;
    }
};

你可能感兴趣的:(Leetcode)