首先發出題目鏈接:
鏈接:https://ac.nowcoder.com/acm/contest/883/B
來源:牛客網
涉及:思維,前綴和
點擊這裏回到2019牛客暑期多校訓練營解題—目錄貼
題目如下
一道比較簡單 的思維題
此題讓我們找到原字符串中‘0’和‘1’數量相同的子串和子序列。
注意子串在原字符串是連續的;而子序列在原字符串是可以不連續的,但是順序不能改變
先討論最長的‘0’‘1’數量相同的子序列:
這個很簡單,數一數原字符串‘0’和‘1’的數量,假設‘0’的數量爲,‘1’的數量爲
那麼滿足條件的子序列最長的長度爲
再討論最長的‘0’‘1’數量相同的子串:
此時有一種方法:將原字符串的‘0’改爲‘-1’,然後求原字符串的前綴和。
由於一個‘1’會和另外一個‘-1’抵消爲0,故前綴和中如果存在兩個相同的數,說明就存在一個‘0’‘1’相同的子串:
解釋:
0與0之間最長隔了2個單位(紅色)
-1與-1之間最長隔了4個單位(綠色)
-2與-2之間最長隔了4個單位(藍色)
所以最長滿足條件子串爲
通過上面的方法,可以給前綴和每一個數標上它的位置,然後以主鍵爲前綴和數值,次主鍵爲每個數的位置進行從小到大排序,求得每兩個相同的數最長隔的單位長度,其中距離最大的單位長度就是答案。具體可以看代碼
例子上面舉了
代碼如下:
#include <iostream>
#include <algorithm>
using namespace std;
int n, word;//n爲題目所給變量,word循環存字符串每一個值
struct Num {//前綴和結構體
int sum;//前綴和的值
int place;//這個值的位置
};
Num num[100005];//前綴和結構體數組
int sum1 = 0, sum0 = 0;//sum1爲'1'的數量,sum0爲'0'的數量
int ans1, ans2 = 0;//ans1爲最長子序列長度的一半,ans2位最長子串長度
bool comp(Num a, Num b) {//以sum爲主鍵,place爲次主鍵
if(a.sum == b.sum) return a.place < b.place;
else return a.sum < b.sum;
}
int main() {
scanf("%d", &n);
num[0].sum = num[0].place = 0;//先將num[0]和num[n+1]初始化
num[n+1].sum = 100001;
for(int i = 1; i <= n; i++) {
scanf("%1d", &word);//循環輸入每一個字符
if(word == 1) {
num[i].sum = (i - 1 == 0? 0: num[i-1].sum) + 1;//更新前綴和數組
sum1++;//記錄字符'1'個數
}
else{
num[i].sum = (i - 1 == 0? 0: num[i-1].sum) - 1;//更新前綴和數組
sum0++;//記錄字符'0'個數
}
num[i].place = i;//記錄每個值的位置
}
ans1 = min(sum1, sum0);//最長子序列長度的一半
sort(num, num + n + 1, comp); //按照上述方法排序
int flag = 0; //記錄每一串前綴和數相同的第一個數的位置
for(int i = 1; i <= n; i++) {
if(num[i].sum != num[i-1].sum) {//記錄相同數開始位置
flag = i;
}
else if(num[i].sum != num[i+1].sum) {//遍歷到相同數最後一個位置
ans2 = max(ans2, num[i].place - num[flag].place);//更新答案
}
}
printf("%d %d", ans2, ans1*2);
return 0;
}