C-密文搜尋
思路:不難。
void solve(){ //C--密文搜尋 可以不是字串雜湊--因為只需要知道相同長度字串對字母出現情況,可以對字串進行!!!排序!!!
string str; cin>>str;
int n,ans=0; cin>>n;
unordered_map<string,int> mp;
for(int i=1;i<=n;i++){
string x; cin>>x;
sort(x.begin(),x.end()); //對字串進行排序!!!
mp[x]++;
}
for(int i=0;i<str.size()-7;i++){
string x=str.substr(i,8);
sort(x.begin(),x.end());
ans+=mp[x];
}
cout<<ans;
}
D-交換次數
思路:十分有技巧!首先最終結果有6種,要對六種可能列舉,這個是容易想到的。
但是怎麼檢查某種排列的代價?
已知A,B,T各自出現的次數,那麼列舉那種情況,就知道哪種情況最終具體是什麼樣的。
例如:輸入 TTA BBT BAATT
情況BAT: BBB AAA TTTTT
定義f1t23,f1t2--f2t1,f2t3,含義為,第一部分要換到2,3部分的個數,第一部分要換到第2部分的個數--第二部分要換到第一部分的個數,第二部分要換到第三部分的個數。
第3部分不用管,因為前面兩部分換好了,第3部分必然也換好了。
考慮3種情況:
①f1t2==f2t1:這種情況最好考慮:cost=f1t23+f2t3
②f1t2>f2t1:這種情況也比較好考慮,先把f2t1換了,再換多餘的f1t2,,和換f1t3和f2t3:cost=f1t23+f2t3
③f1t2<f2t1:這種情況因為沒有記錄第三部分的情況,所以相對比較繞:cost=f1t23-f1t2+f2t1+f2t3--這裡的f1t23-f1t2=f1t3,即cost=f1t3+f2t1+f2t3
第三種情況先f1t3是為了把在第三部分的,第二部分的字母,換到第一部分,然後進行f2t1的時候就可以換上。
string str;
int n,ans=INT_MAX,cnt[3];
unordered_map<char,int> mp;
string pos[6]={"ABT","ATB","BAT","BTA","TAB","TBA"};
void check(char a,char b,char c){
int f1t23=0,f1t2=0,f2t1=0,f2t3=0;
for(int i=0;i<cnt[mp[a]]+cnt[mp[b]];i++){
if(i<cnt[mp[a]]){
if(str[i]!=a) f1t23++;
if(str[i]==b) f1t2++;
}
else{
if(str[i]==a) f2t1++;
if(str[i]==c) f2t3++;
}
}
if(f1t2==f2t1) ans=min(ans,f1t23+f2t3);
else if(f1t2>f2t1) ans=min(ans,f1t23+f2t3);
else ans=min(ans,f1t23-f1t2+f2t1+f2t3);
}
void solve(){ //補D--交換次數--nb
//列舉6種情況容易想到,問題是怎麼check某種情況下的代價。
cin>>str; n=str.size();
mp['A']=0,mp['B']=1,mp['T']=2;
for(int i=0;i<n;i++){
if(str[i]=='A') cnt[mp[str[i]]]++;
else if(str[i]=='B') cnt[mp[str[i]]]++;
else cnt[mp[str[i]]]++;
}
for(int i=0;i<6;i++) check(pos[i][0],pos[i][1],pos[i][2]);
cout<<ans;
}
E-k倍區間
思路:錯了又錯的老題。。見註釋。
void solve(){ //補E--K倍區間--在牛客還是cf,做過一模一樣的題目,當時也是補的題。現在再遇到,還是沒想到,還是補題。。
//字首和pre;
//如果某個區間[L,R]的和是K的倍數;
// 那麼(pre[R]-pre[L-1])%k=0;
//pre[R]%k-pre[L-1]%k=0;
//pre[R]%k = pre[L-1]%k;
//答案顯而易見,只需記錄字首和中出現相同餘數的數字,兩兩配對的對數即是ans;
int n,k,ans=0; cin>>n>>k;
int sum=0;
unordered_map<int,int> mp;
//mp[0]=1; //init
for(int i=1;i<=n;i++){
int x; cin>>x;
sum=(sum+x)%k;
ans+=mp[sum]; //到目前,前面有幾個現在這個餘數,就可以構成幾個合法區間.
if(sum==0) ans++; //sum剛剛好為0的時候,代表這個區間不用減去前面某段,從1到cur剛剛好是k的倍數。當然也可以減去前面某段,就是減去前面也是0的區間。
mp[sum]++; //這個餘數出現次數加一
}
cout<<ans;
}
void solve(){ //補E--K倍區間--在牛客還是cf,做過一模一樣的題目,當時也是補的題。現在再遇到,還是沒想到,還是補題。。
int n,k,ans=0; cin>>n>>k;
int sum=0;
unordered_map<int,int> mp;
for(int i=1;i<=n;i++){
int x; cin>>x;
sum=(sum+x)%k;
mp[sum]++; //這個餘數出現次數加一
}
for(int i=0;i<k;i++) ans+=(mp[i]*(mp[i]-1)/2); //組合--Cm2:m箇中選兩個配對;Cm2=m*(m-1)/2;
ans+=mp[0]; //剛剛好為0的時候,代表這個區間不用減去前面某段,從1到cur剛剛好是k的倍數。當然也可以減去前面某段,就是減去前面也是0的區間(上面迴圈已經算過了).
cout<<ans;
}
G-包子湊數
思路:正解是類似揹包的dp,我的寫法不是正解,但是類似。
void solve(){ //G--包子湊數 siwei?
//似乎不是正解...要設好引數範圍
//一開始用vector來維護能組合出來的數字。
//但是判斷一個數x的能否被組合出來時,時間複雜度過高--用兩層迴圈列舉vct所有數字組合---o(n^2)。
//改成了set之後,既避免了重複數字,還能o(nlogn)複雜度下判斷x能否被組合出來
//引數範圍ok的話,可以AC
int n; cin>>n;
set<int> st;
int minn=200;
bool checkone=false;
for(int i=1;i<=n;i++){
int x; cin>>x;
st.insert(x);
minn=min(minn,x);
if(x==1) checkone=true;
}
if(checkone){
cout<<"0";
return;
}
int ans=0;
set<int> test;
st.insert(0); //便於查詢本身存在於st的x
for(int x=1;x<=10000;x++){ //引數範圍--1000太小,會wa兩個點--5,6,7,8,9000太小,wa一個點--10000夠了
bool check=false;
for(auto s:st){
if(st.find(x-s)!=st.end()){
st.insert(x);
check=true;
break;
}
}
if(!check) {
test.insert(x);
ans++;
}
}
bool check=false;
int cur=1;
for(auto p=st.begin();p!=st.end();p++){
if(p==st.begin()) continue;
if(*p-*prev(p)==1) cur++;
else cur=1;
if(cur==minn){ //判斷能組合出來的數字,是否有minn個連續的數字,有的話後面所有數字都可以推出來。
//--否則可以判定為INF,雖然不是完全嚴謹,但是在搜尋1e4個數字後還沒有連續minn個數字,就認為是INF了
check=true;
break;
}
}
if(check) cout<<ans;
else cout<<"INF";
}
H-疑惑和之和
思路:拆位+貢獻+字首和。比較有價值的一題
int n,ans=0;
int arr[100005];
void solve(){ //補H-異或和之和 (二進位制)拆位+貢獻法 o(20*n) 學習新思想
cin>>n;
for(int i=1;i<=n;i++) cin>>arr[i];
for(int j=0;j<=20;j++){ //二進位制位下的位置
int sum=0,cntodd=0,cnteven=0;
for(int i=1;i<=n;i++){
if(arr[i]>>j&1) sum++;
if(sum&1) { ////如果現在這一位為1的字首和是奇數,那麼這個數字就有前面(cnteven+1)個合法區間在這一位上有貢獻
cntodd++;
ans+=(1<<j)*(cnteven+1); ////每個總和為奇數的區間,都會在這一位上有貢獻
}
else { ////如果現在這一位為1的字首和是偶數,那麼這個數字就有前面cntodd個合法區間在這一位上有貢獻
cnteven++;
ans+=(1<<j)*cntodd; ////每個總和為奇數的區間,都會在這一位上有貢獻
}
}
}
cout<<ans;
}