题目描述 :
本来这一场比赛是小白月赛,但是因为嘤嘤的题目越出越难,就只能变成练习赛了呢!但大部分的题目还是小白难度,相信大家都能AK,但是为了以防万一,嘤嘤还是放一题血签吧!
嘤嘤突然发现,今天好像是一个攻击性很强的日子,除此之外,下个月的今天和上上上个月的今天都是很臭的日子,上上个月的今天又是小木曾雪菜的生日,上个月的今天还是 π 节。
世界名画——《你为什么这么熟练啊!》(妮露(左),神里绫华(中),荧(右))
在op414、哼哼啊啊啊、届不到、高等(su)数(tui)学的轮番刺激下,嘤嘤已经对同时出现1、4这两个数字的数字串PTSD了,嘤嘤嘤!
题目太抽象,我们分析后得到以下结论:
给你一个字符串,问你这个字符串中没有同时出现‘1’和‘4’的字串个数。
是不是很简单呀???NONONO。
问题在于,这个题目的时间要求
不然还真挺简单的(笑)
所以!我们必须用O(n^2)复杂度以下的方法来实现!
那么,再仔细分析一下我们可以得知~
只要能想到我们要判断数组中,‘1’和‘4’出现的位置,来进一步优化算法。
你就成功一大半了! 讲一种解法。
我们先记录数组中‘1’和‘4’出现的位置,实现如下
for(int i=1;i<=n;i++){//n是字符串长度
a[s[i]] = i;//记录数字出现的位置
//等会再告诉你
}
现在,我们可以很容易得知,
最后一个1和4的位置中靠左的那一个往右都是合法的。
例如,041004080,枚举第8个位置时,最后一个1在第3个位置,最后一个4在第6个位置,最左边的位置是3,因此,00408,0408,408,08,8都是合法子串。
所以,我们可以得出 只要用当前位置(即 i )减去 最后一个‘1’或‘4’中位置靠前的位置 即储存过的a['1']或a['4']中较小的那个。就可以得出所有合法的字串。(因为这个字串里必然不会同时存在1或者4。)(顺便一提,n个字符组成的字符串,必然可以分为n-1个字串,加上自己就是n个)
得出以下式子:ans+=i-min(a['1'],a['4'])
补全循环
for(int i=1;i<=n;i++){
a[s[i]] = i;
ans += i - min(a['1'] , a['4']);
}
看不懂?再举一个例子
比如字符串 023114514
遍历第六个位置时,i=5,a['4']=5,a['1']=4。那么min(a['1'],a['4'])=4,符合条件的子串为‘4’,‘023114’‘23114’‘3114’‘114’‘14’均不符合。仅有一个字串符合条件,i-min(a['1'],a['4'])得出的答案也是1。
遍历第七个位置时,i=6,a['4']=5,a['1']=4。那么min(a['1'],a['4'])=4,符合条件的字串为‘45’‘5’,因为‘0231145’‘231145’‘31145’‘1145’‘145’均不符合。仅有两个字串符合条件,i-min(a['1'],a['4'])得出的答案也是2。
之后我们补全剩余代码即可
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
int n;
cin>>n;
string s;
cin>>s;
s = " " + s;
vector<int> a(200);
LL ans = 0;
for(int i=1;i<=n;i++){
a[s[i]] = i;
ans += i - min(a['1'] , a['4']);
}
cout<<ans<<endl;
return 0;
}