题干:
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
小N现在有一个字符串S。他把这这个字符串的所有子串都挑了出来。一个S的子串T是合法的,当且仅当T中包含了所有的小写字母。小N希望知道所有的合法的S的子串中,长度最短是多少。
输入描述:
一行一个字符串S。只包含小写字母。S的长度不超过106.
输出描述:
一行一个数字,代表最短长度。数据保证存在一个合法的S的子串。
示例1
输入
复制
ykjygvedtysvyymzfizzwkjamefxjnrnphqwnfhrnbhwjhqcgqnplodeestu
输出
复制
49
解题报告:
尺取,或者更新最后一次出现的坐标也可以(我叫他桶标记法,类似的题目HDU 3333 SPOJ DQUERY)
错误代码:
#include<bits/stdc++.h>using namespace std;
char s[1000005];
int vis[30];
int main()
{scanf("%s",s);int len = strlen(s);memset(vis,0,sizeof vis);int l = 0,r = 0,cnt = 0,minn = 0x3f3f3f3f;while(r<len) {if(vis[s[r]-'a'] == 0) {cnt++;}vis[s[r]-'a']++;if(cnt == 26) break;r++;}while(r < len) {vis[s[r]-'a']++;while(vis[s[l] - 'a'] > 1) {vis[s[l] - 'a']--;l++;}minn = min(minn,r-l+1);r++;//先更新minn,再更新r}printf("%d\n",minn);return 0 ;}
// qwertyuioplkjhgfdsazxcvbnm
总结:
刚开始自己写的这个代码有点想少了,本来是想都放在一个while中的,但是感觉这样逻辑结构不是很清晰,然后就想把它新开一个while放进去,但是在边界处理的时候出现了问题,那就是我没有我在cnt==26的时候break了,但是r++这步没有执行,然后进入第二个while,又vis++了一次,也就是说同一个s[r],我们在vis数组中计算了两次。
而你不能直接把r++放到if(cnt == 26) break;前面,因为这样的话,出现的第二个问题就是,比如你输入aaaabcdefg....xyz,那么扫到最后一个字符r++,break。然后就进不去下面的while循环了、、因为这时的r==len,所以进不去这个循环,l就没法前移,就找不到最短的解,于是会wa。
AC代码1:(自己写 ,有点麻烦了)
#include<bits/stdc++.h>using namespace std;
char s[1000005];
int vis[30];
int main()
{scanf("%s",s);int len = strlen(s);memset(vis,0,sizeof vis);int l = 0,r = 0,cnt = 0,minn = 0x3f3f3f3f;while(r<len) {if(vis[s[r]-'a'] == 0) cnt++;vis[s[r]-'a']++;if(cnt == 26) break;r++;}while(vis[s[l] - 'a'] > 1) {vis[s[l] - 'a']--;l++;}minn = min(minn,r-l+1);r++;while(r < len) {vis[s[r]-'a']++;while(vis[s[l] - 'a'] > 1) {vis[s[l] - 'a']--;l++;}minn = min(minn,r-l+1);r++;//先更新minn,再更新r } printf("%d\n",minn);return 0 ;}
// qwertyuioplkjhgfdsazxcvbnm
AC代码2:(在线更新坐标的思想)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <string>
#include <deque>
#include <set>
#include <queue>
using namespace std;
#define ll long long
#define N 1000009
#define gep(i,a,b) for(int i=a;i<=b;i++)
#define gepp(i,a,b) for(int i=a;i>=b;i--)
#define gep1(i,a,b) for(ll i=a;i<=b;i++)
#define gepp1(i,a,b) for(ll i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define P pair<int,int>u+
char s[N];
int loc[30];
int main()
{scanf("%s",s);int l=0;int len=strlen(s);int ans=len+1;mem(loc,-1);int cnt=0;//只要[l,i]区间里含有26个字母即可gep(i,0,len-1){if(loc[s[i]-'a']==-1){cnt++;} loc[s[i]-'a']=i;//该字母最大的坐标while(l<loc[s[l]-'a']) l++;//后面有了,那么前面的就可以不用了,l++。减小去区间长度if(cnt==26){ans=min(ans,i-l+1);}}printf("%d\n",ans);return 0;
}
AC代码3:
#include <bits/stdc++.h>
#define N 1000010
#define INF 0x3f3f3f3f
using namespace std;int main()
{int i,l=0,ans=INF;int vis[200]={0};string s;set<char>myset;cin>>s;for(i=0;s[i];i++){myset.insert(s[i]);vis[s[i]]++;while(vis[s[l]]>1)vis[s[l]]--,l++;if(myset.size()==26)ans=min(ans,i-l+1);}cout<<ans<<endl;return 0;
}