US 2015 Febrary BRONZE 题解

这次的题目倒简单了很多,大部分的思路比较直观,一下子就能想出来一个比较优的算法。所以也不说那么多了,直接上题解:

Problem 1. censor

审查:

FJ订阅了Good Hooveskeeping杂志给他的奶牛在挤奶时看,不幸的是,上面有一篇关于要和谐掉的做牛排的文章,FJ强烈要求不要给他的奶牛们看到。(很明显,这个杂志需要广电总局)

FJ把整篇文章的文本整合为一个字符串S,最多有10^6个字符。现在FJ要求你移除掉子串T,长度<=100,来去掉“不恰当”的内容。FJ是这么干的:找到最早出现在S中的T,把它删掉,然后重复、重复再重复……不断地删掉第一个T,直到S中没有T了。注意:删除之后,可能出现在删除之前没有出现的的T

 

FJ输出最后的S

 

输入(censor.in)

字符串S

字符串T

(字符是a..z

 

输出(censor.out)

和谐之后的S

S不会是空串

 【题目分析】

这一题有一个最直观的框架(随便写的,理解一下意思就好):

while (S.find(T) != -1){
S.erase(S.find(T),T.size());
}

但是这毕竟太慢了,有很多不必要的过程,特别是时间复杂度很高,分析了一下,大概是O(|T|*|S|*|S|)(|T|表示T的长度),比如说在S=“aaaaaaaaaaaaaaaaaaaaaaaaa”和T="a"时最差。解决这个问题,主要能够从三个方面去优化:
【删除优化】
可以开一个链表,以存储相应的字符串,这样的话删除就可以快很多,只需要将中间的指针赋值就好了。这样的话就可以省去删除的复杂度,优化效果很好,可以将时间复杂度做到O(|T|*|S| )。但是还是挺高的。所以就要从搜索上入手了。

 【搜索优化】

显而易见的,利用哈希思想的查找速度能够大大超越普通的查找,加上之前的删除优化,时间复杂度直接就做到了O(|T|+|S|)。当然也可以用KMP,但是我们现在还没有学,其实哈希也差不多的。

【常数级的输入优化】

因为输入规模还算大,10^5的长度,可以考虑用一下getchar(),来加快一下速度。但其实没有什么太大的用处……


当然这些优化确实很简单,但是下面这里有一个更好的优化,至少编起来简单不少。假如我们再造一个字符串R,然后做一个循环将S中的字符依次放入R中。注意到R中如果有出现T的话,必定是在尾部。举个例子,一个比较考验程序的数据S=“ABABCC”,T=“ABC”,要出现"ABC"就只能是在最后面,那就在R="ABABC"时马上去掉了ABC,变成了R="AB",最后再放进去一个“C”,就全部删掉了。所以下面给出一段代码:

【代码】

# include <cstdio>
# include <string>

using namespace std;
const int MAXS = 100001;
const int MAXT = 101;
string S,T;
string NowString;

void readStr(string& str){
char c;
while (true){
c = getchar();
if (c == EOF || c == '\n'){
break;
}
str.push_back(c);
}
}

void writeStr(const string& str){
int Pos = 0;
int Len = str.size();
while (Pos != Len){
putchar(str[Pos]);
++Pos;
}
}

int main(){
freopen("sensor.in","r",stdin);
freopen("sensor.out","w",stdout);
//Init
S.clear();
T.clear();
NowString.clear();
readStr(S);
readStr(T);

//Start Process
int SLen = S.size();
int TLen = T.size();
for (int i=0;i<SLen;++i){
NowString.push_back(S[i]);
if (NowString.size() >= TLen && NowString.substr(NowString.size() - TLen) == T){
NowString.resize(NowString.size() - TLen);
}
}
//Output
writeStr(NowString);
}


应该说,这道题目我用这种做法主要是利用了尾部删除的低时间复杂度,本质上还是没有Hash的快,但是这道题目的数据太水了,没有给类似于R=”ABABABAB……(10000个AB)……ABCCCCC……(10000个C)C“,然后T=“ABC”的数据。或者说T特别长的数据,可能常数的优化还是有一点功劳的吧。反正写起来挺短的。


Problem 2. COW

我们的老朋友奶牛Bessie无意中在她最喜欢的放牧场地中央发现一个刻在一块大石头上面的耐人寻味的碑文。碑文的文字似乎是一种神秘古老的语言。这种语言总共只有三个大字COW

尽管Bessie无法破译这种语言,但她看到COW按顺序形成她最喜欢的一句话COW,她还是会很爽很爽,于是她想知道COW在这段碑文中总共出现了多少次。

如果有COW中穿插了其他字符,但是字符C,O,W按照正确的顺序出现Bessie不会介意。如果不同的COW共享一些字母,她也不会介意。

例如,牛在CWOW中出现一次,在CCOW中出现两次,在CCOOWW中出现8

由于碑文的内容太过于深奥,智商不高的Bessie看了头脑发胀,于是她找到了聪明的你,请你帮Bessie找出COW出现多少次。

 

输入格式:(文件cow.in

输入的第一行包含一个整数N <= 10 ^ 5。第二行包含字符串的N个字符,每个字符是一个CO,或W.

 

输出格式:(文件cow.out

COW不一定要连续出现,按顺序出现也算作出现一次

 

 

输入样例:

6

COOWWW

 

输出示例:

6

 

[Problem credits: Ben Cousins and Brian Dean, 2015]

 【题目分析】

这道题挺简单的,听杨鸿飞说是一道叫“FBI树”的题。比如说我们设三个变量NC,NO,NW,初值都为0。接着NC遇到字符C都加一,NO遇到字符O都加一个NC。这是为什么呢?因为每遇到一个字符O,就会和前面的所有的字符C配成NC对,所以这相当于寻找有多少个“CO”。接着,NW则是每遇到一个字符W就加上一个NO,同样的道理,这是和前面所有的“C”-“O”对配成NO个“COW”对。光说可能说不清楚,直接上一个例子:”COOWWW“。其中,NC遇到一个C,所以值为1。然后后面的两个O都可以和这一个C配成一对,所以说NC=NC+NC=2。再接着后面的3个W,各自又可以凑上前面已经配对好的“C”-“O”对,也就是NW=NC+NC+NC=6。NO,就是指“C”的个数,NC,就是指“C”-“O”对的个数,这里的“C”-“O"对只要顺序相同即可。而NW,就是指”C“-”O“-”W“对的个数!所以给出代码:

【代码】

# include <iostream>
# include <cstdio>
# include <string>
using namespace std;
string S;
void readStr(string& str){
char c;
while (true){
c = getchar();
if (c == '\n' || c == EOF){
break;
}
str.push_back(c);
}
}

int main(){
freopen("cow.in","r",stdin);
freopen("cow.out","w",stdout);
readStr(S); //No Using of N
readStr(S);

unsigned long long int nc,no,nw;
nc = no = nw = 0;
int Len = S.size();
for (int i=0;i!=Len;++i){
if (S[i] == 'C'){
//C就直接加
++nc;
}
else if (S[i] == 'O'){
//O就接上之前的所有C的量
no += nc;
}
else if (S[i] == 'W'){
//W就接上所有O的量
nw += no;
}
}

cout << nw << endl;
}


就这点数据量,懒得用输出优化了……读入优化也只是习惯了,正好复制上去,100的长度倒也没有什么大的问题。


Problem 3.Hopscotch

 

就像人类喜欢玩“跳房子”的游戏,农夫约翰的奶牛们发明了游戏的一个变种,自己玩。因为是笨重得近1吨的奶牛玩,它们跳房子几乎总是以灾难告终,但意外地,这并没有阻止奶牛尝试每天下午去玩。

 

游戏是在一个R*C(2<=R<=15,2<=C<=15)的方格上进行,其中每个正方形是红色或蓝色的。奶牛从左上角开始,然后由一个跳跃的序列移动到右下角,如果一个跳跃是有效的,只有当

1)你跳到一个不同颜色的正方形,

 

2)你要跳到的方格在当前你所在方格的下面至少1

 

3) 你要跳到的方格在当前你所在方格的右边至少1

 

输入格式:

 

第一行包含两个整数RC,接下来R行会包括C个字符,每个字符是'R''B',代表一个红色或蓝色的格子。

 

输出格式(hopscotch.in)

 

输出可以从左上角跳到右下角的方案数

 

样例输入(hopscotch.in)

 

4 4

RRRR

RRBR

RBBR

RRRR

 

样例输出(hopscotch.out)

3

 【题目分析】

这题绝对是最水的,没有之一,想都不想交个搜索上去就能过了。只要颜色相同就可以了,数据也太小了,几乎不用优化了(但我还是用了输入优化……)。

【代码】

# include <cstdio>
using namespace std;
const int MAXRC = 16;
char grid[MAXRC][MAXRC];
int R,C;
void readMatrix(){
for (int i=0;i!=R;++i){
for (int j=0;j!=C;++j){
grid[i][j] = getchar();
if (grid[i][j] == '\n'){
grid[i][j] = getchar();
}
}
}
}

int Jump(const int& x,const int& y){
if (x == R - 1 && y == C - 1){
//Last Step
return 1;
}

int sum = 0;
for (int i=x+1;i!=R;++i){
for (int j=y+1;j!=C;++j){
if (grid[x][y] != grid[i][j]){
sum += Jump(i,j);
}
}
}
return sum;
}

int main(){
freopen("hopscotch.in","r",stdin);
freopen("hopscotch.out","w",stdout);
scanf("%d %d",&R,&C);
readMatrix();
printf("%d\n",Jump(0,0));
}


这道题只花了我十分钟就做好了,还有五分钟时间是中间没有打上那两个”-1“,忘记了从0开始了……

【总结】

这次的题目偏基础一点,三两下就做出来了,不用多想。但是如果数据范围足够大的话还是蛮恐怖的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值