#737. 徐老师的字符串(微软和阿里巴巴面试原题改编)

说明


徐老师最近做了一道题:求每个字母最多只出现一次的最长子串

他觉得这道题还不够有趣,于是他想到一个新题目:

对于一个字符串,求每个字母出现次数为偶数的最长子串

现在他用这道题来考考你,请找出这个最长子串的长度,并输出这个子串(如果有多个子串的长度相同,请输出从左往右最早出现的那一个)

输入格式


输入一行字符串 SS,保证只包含小写字母

对于 20% 的数据,1  <=|S|∣≤600

对于 30% 的数据,1<=ISI<=10000

对于 100% 的数据,1 ∣S∣≤200000

输出格式


输出第一行一个整数,表示最长的子串长度
输出第二行表示这个字符串的下标 [l,r][l,r]

样例

输入数据 1

bdaaadabd

Copy

输出数据 1

8
0 7

=========================================================================

这道题有种做法,时间复杂度依次降低

一,暴力解法

直接三重循环,第一层for枚举起始位置,第二层for枚举结束位置,第三层for枚举26个字母,第4层for循环枚举[i,j]的区间内查看第三层循环中的字母出现了几次,然后在第三层循环中看次数是否为偶数,如果是偶数,则看区间大小是否>先前记录的长度即可,

时间复杂度:O(n^3)。可以说:前20%的数据就是为了暴力解法可以骗到分而设置的。

二,前缀和优化

 如图所示,我们可以用一个二维数组来记录26个字母出现的次数,这样,我们就可以用这种预处理的方法把判断一个区间内是否为偶数的那个循环去掉。

方法:(mp[j][t] - mp[i - 1][t])

代码:

#include <bits/stdc++.h>
using namespace std;
char a[200010];
int len,m,l,sta,en,mp[200010][30];
int main()
{
  cin>>a;
  int l = strlen(a);//长度
  for(int i = l; i > 0; i--) a[i] = a[i - 1];//让a数组从1的下标开始
  for(int i = 1; i <= l; i++)
  {
    for(int j = 1; j < 27; j++) mp[i][j] = mp[i - 1][j];
    mp[i][a[i] - 'a' + 1] = 1 + mp[i - 1][a[i] - 'a' + 1];//预处理前缀和
  }
  /*for(int i = 1; i <= a.size(); i++)
  {
    for(int j = 1; j < 27; j++)
      cout<<mp[i][j]<<" ";
    cout<<endl;
  }*/
  for(int i = 1; i <= l; i++)//起始点
    for(int j = i + 1; j <= l; j++)//结束位置
    {
      m = 0;
      for(int t = 1; t < 27; t++)
        if((mp[j][t] - mp[i - 1][t]) % 2 == 1)//不为偶数
          m = 1;//就标记
      if(m == 0 && len < (j - i + 1))//如没被标记,并且当前的长度>以前的长度
      {
        len = j - i + 1;
        sta = i;
        en = j;
      }
    }
  if(len == 0) sta = 1,en = 1;//特判
  cout<<len<<endl<<sta - 1<<" "<<en - 1;
  return 0;
}

时间复杂度:O(n^2),前30%的数据可以过了

三,简单数学知识+map优化

我们可以用前缀和快速计算出某个区间内的字母出现次数是否为偶数
再加上一个简单的数学性质,奇数 - 奇数 = 偶数,偶数 - 偶数 = 偶数
那我们需要知道的只是两个一个区间内所有字母出现次数为偶数,那其实就是找到所有字母出
现次数奇偶性一样的两个位置,就可以组成满足条件的区间
我们需要尽可能长的字符串,那必然是用最远的两个数字组成,那么当枚举到 i 的时候如果所
有字母状态为 X,我们一定是找到 X 状态最早出现的位置 j 作为左端点
那么我们直接用 map 记录每个状态最早出现的位置即可,第二次出现就记录答案
代码:
#include<bits/stdc++.h>
using namespace std;
map<int, int> mp;
char s[200000];
int main()
{
  cin>>s;
  int n = strlen(s),now = 0,ans = 0,l,r;
  for (int i = 0; i < n; ++i)
  {
    now ^= 1 << (s[i] - 'a');
    int pos = mp[now];
    if (pos != 0 || now == 0)
    {
      if (i + 1 - pos > ans)
      {
        ans = i + 1 - pos;
        l = pos;
        r = i;
      }
    }
    else mp[now] = i + 1;
  }
  cout<<ans<<endl<<l<<" "<<r;
  return 0;
}

时间复杂度:O(n)!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值