1. 程式人生 > >A】字串(尺取法,桶標記法)

A】字串(尺取法,桶標記法)

題幹:

時間限制: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;
}