1. 程式人生 > >[隊內測試Day10.26][P97]T1 字串+字首和

[隊內測試Day10.26][P97]T1 字串+字首和

大意:

給定一字串,定義權值為串中出現次數最多字元 - 出現次數最少字元,求權值最大子串
字串長度 n <= 10^6,保證都由小寫字母組成

考完試悔的腸子都青了的題……
明明離正解那麼近了……

考場思路:
發現求區間l,r中字元a出現次數-字元b出現次數,用字首和可以表示為

SraSla(SrbSlb)
SraSrb(SlaSlb)

而我們發現,min(SaSb)是可以被維護的……
又因為掃描到某一位置時,作為最多字元更新目前最優答案的只可能是該位置上的字元
所以每一位置for一遍26個字元,尋找可能作為最小值的字元

還有問題……
雖然維護了m

in(SaSb),並且可以保證該位置出現在當前位置之前
但不一定該位置與當前位置組成的區間裡存在字元b
對此的處理方法:記錄b最後出現的位置和min(SaSb)的位置
因為注意到min(SaSb)的性質,是隻有在Sb更新時才會增加的
所以min(SaSb)的位置一定為b存在的位置
如果Posmin(SaSb)=PosLastb,就讓答案強行 - -,表示b + 1

%%%考場正解的summer && wyh

後來wyh講了更直觀的思路
“要suma和sumb的差值最大,就減去sumb比suma多的最多的區間”
ssssrO

#include<iostream>
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; char s[1000010]; int n; int sum[30],mnum[30][30],mpos[30][30]; int last[1000010]; int ans; int main(){ freopen("a.in","r",stdin); freopen("a.out","w",stdout); scanf("%d",&n); scanf("%s",s + 1); memset
(last,-1,sizeof(last)); for(int i = 1;i <= n;i ++){ int t = s[i] - 'a'; sum[t] ++; last[t] = i; for(int k = 0;k <= 25;k ++){ if(k == t)continue; if(mnum[k][t] > sum[k] - sum[t]) mnum[k][t] = sum[k] - sum[t],mpos[k][t] = i; } for(int k = 0;k <= 25;k ++){ if(k == t || !sum[k])continue; int tmp = sum[t] - sum[k] - mnum[t][k]; if(mpos[t][k] == last[k])tmp --; ans = max(ans,tmp); } } printf("%d",ans); fclose(stdin); fclose(stdout); return 0; }

明明有思路的題放著不打,自己去T2找噁心……
但後來檢查考場程式碼思路確實凌亂……

Tips:
用適當的變數名代替表示式使程式碼更簡潔
對自己的思路有信心……