Tag Archives: 字符串

hihoCoder week 58-1-Beautiful String

hihoCoder week 58-1-Beautiful String 题目1 : Beautiful String 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 We say a string is beautiful if it has the equal amount of 3 or more continuous letters (in increasing order.) Here are some example of valid beautiful strings: “abc”, “cde”, “aabbcc”, “aaabbbccc”. Here are some example of invalid beautiful strings: “abd”, “cba”, “aabbc”, “zab”. Given a string of alphabets containing only lowercase alphabets (a-z), output “YES” if the string contains a beautiful sub-string, otherwise output “NO”. 输入 The first line contains an integer number between 1 and 10, indicating how many test cases are followed. For each test case: First line is the number of letters in the string; Second line is the string. String length is less than 10MB. 输出 For each test case, output a single line “YES”/”NO” to tell if the string contains a beautiful sub-string. 提示 Huge input. Slow IO method such as Scanner in Java may get TLE. 样例输入 4 3 abc 4 aaab 6 abccde 3 abb 样例输出 YES NO YES NO


本题考察字符串的基本知识。题目给出了漂亮字符串的定义,形如aa…bb…cc,保证至少3个连续升序的字符串,并且每个字符的个数相同。仔细观察可以发现,虽然aaaabbccc不是漂亮字符串,但其子串aabbcc是漂亮的,所以只要保证b的数量小于等于a和c的数量即可。 所以解法就是遍历字符串,计算每个字符重复出现的次数,判断连续3种字符是否为连续升序,且满足中间字符数量大于两边。算法复杂度为O(N)。 完整代码如下: [cpp] #include<iostream> #include<cstdio> #include<string> using namespace std; const int kMaxN = 10 * 1024 * 1024 + 5; char line[kMaxN]; int n; char same_letter[kMaxN]; int same_length[kMaxN]; bool check(int k) { if ((same_letter[k] – 1 == same_letter[k – 1]) && (same_letter[k – 1] – 1 == same_letter[k – 2])) if (same_length[k – 1] <= same_length[k] && same_length[k – 1] <= same_length[k – 2]) return true; return false; } bool IsBeautiful() { if (n < 3) return false; else { int i = 0,k=0; while (i < n) { int j = i + 1; while (j < n && line[j] == line[i]) j++; same_letter[k] = line[i]; same_length[k] = j – i; if (k >= 2) { if (check(k)) return true; } k++; i = j; } return false; } } int main() { int t; scanf("%d", &t); while (t–) { scanf("%d", &n); scanf("%s", line); if (IsBeautiful()) printf("YESn"); else printf("NOn"); } return 0; } [/cpp] 本代码提交AC,用时293MS,内存37MB。]]>

POJ 1068-Parencodings

POJ 1068-Parencodings Parencodings Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 20210 Accepted: 12196 Description Let S = s1 s2…s2n be a well-formed string of parentheses. S can be encoded in two different ways: q By an integer sequence P = p1 p2…pn where pi is the number of left parentheses before the ith right parenthesis in S (P-sequence). q By an integer sequence W = w1 w2…wn where for each right parenthesis, say a in S, we associate an integer which is the number of right parentheses counting from the matched left parenthesis of a up to a. (W-sequence). Following is an example of the above encodings: S (((()()()))) P-sequence 4 5 6666 W-sequence 1 1 1456 Write a program to convert P-sequence of a well-formed string to the W-sequence of the same string. Input The first line of the input contains a single integer t (1 <= t <= 10), the number of test cases, followed by the input data for each test case. The first line of each test case is an integer n (1 <= n <= 20), and the second line is the P-sequence of a well-formed string. It contains n positive integers, separated with blanks, representing the P-sequence. Output The output file consists of exactly t lines corresponding to test cases. For each test case, the output line should contain n integers describing the W-sequence of the string corresponding to its given P-sequence. Sample Input 2 6 4 5 6 6 6 6 9 4 6 6 6 6 8 9 9 9 Sample Output 1 1 1 4 5 6 1 1 2 4 5 1 1 3 9 Source Tehran 2001


简单题。给一个括号字符串的P序列,要求算出其W序列。 直接计算即可,首先根据P序列还原出该括号字符串,因为P序列的每一个数字是对应右括号的,所以这个数字就表示这个右括号左边有多少个左括号,通过一个while循环就可以还原出原始括号字符串。 得到原始括号字符串之后,就可以计算W序列了。W序列的每一个数字也是对应右括号的,它表示从“和该右括号匹配的左括号”的位置到“该右括号”的位置一共有多少个右括号。这就要求我们知道每一个右括号匹配的左括号的位置,用一个堆栈可以简单求出。 求除了右括号及其匹配的左括号的位置后,就可以遍历括号字符串计算出期间共有多少个右括号了。 求解思路清晰之后,可以快速写出完整代码: [cpp] #include<iostream> #include<string> #include<vector> #include<stack> using namespace std; typedef struct PAR//括号结构 { char c;//左括号或右括号 int pos;//该字符的下标 }; //根据P-sequence还原出括号字符串 string get_parentheses(vector<int>& p) { string rs=""; vector<int>::iterator it=p.begin(); int i=0; while(it!=p.end()) { while(i!=(*it)) { rs+='(‘; i++; } rs+=’)’; it++; } return rs; } //根据括号字符串paren得到W-sequence void get_w(vector<int>& w,string paren) { int len=paren.size(); stack<PAR> s_p; PAR par; vector<int> right_pos,left_pos;//分别记录某个右括号对应左括号的位置 for(int i=0;i<len;i++) { if(paren[i]=='(‘)//如果是左括号,入栈 { par.c=paren[i]; par.pos=i; s_p.push(par); } else//右括号,出栈,配对 { par=s_p.top(); s_p.pop(); right_pos.push_back(i); left_pos.push_back(par.pos); } } vector<int>::iterator it_r=right_pos.begin(); vector<int>::iterator it_l=left_pos.begin(); while(it_r!=right_pos.end())//计算W-sequence { int num=0; for(int i=*it_l;i<=*it_r;i++) { if(paren[i]==’)’) num++; } w.push_back(num); it_r++; it_l++; } } int main() { int t,n; cin>>t; while(t–) { cin>>n; vector<int> p;//P-sequence int tmp; string parentheses; while(n–) { cin>>tmp; p.push_back(tmp); } parentheses=get_parentheses(p); vector<int> w;//W-sequence get_w(w,parentheses); vector<int>::iterator it=w.begin(); while(it!=w.end()) { cout<<*it<<" "; it++; } cout<<endl; } return 0; } [/cpp] 本代码提交AC,用时0MS,内存764K。]]>

POJ 2503-Babelfish

POJ 2503-Babelfish Babelfish Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 33433 Accepted: 14347 Description You have just moved from Waterloo to a big city. The people here speak an incomprehensible dialect of a foreign language. Fortunately, you have a dictionary to help you understand them. Input Input consists of up to 100,000 dictionary entries, followed by a blank line, followed by a message of up to 100,000 words. Each dictionary entry is a line containing an English word, followed by a space and a foreign language word. No foreign word appears more than once in the dictionary. The message is a sequence of words in the foreign language, one word on each line. Each word in the input is a sequence of at most 10 lowercase letters. Output Output is the message translated to English, one word per line. Foreign words not in the dictionary should be translated as “eh”. Sample Input dog ogday cat atcay pig igpay froot ootfray loops oopslay atcay ittenkay oopslay Sample Output cat eh loops Hint Huge input and output,scanf and printf are recommended. Source Waterloo local 2001.09.22


水题。题目说明了“No foreign word appears more than once in the dictionary”,又要快速查找,马上想到用MAP来当字典,然后查找。这题稍微有点麻烦是的输入输出,因为题目没有给出具体的输入结束标志,需要自己判断,输入的又是字符串,且含有空格空行等,虽然题目hint建议用printf或scanf,但是我选择了用C++的getline,看起来更加优雅。 getline的优点是一次读入一行,不管有没有空格,而且它还能读入空行,所以我们可以使用下面的代码来读取一行并判断跳出循环条件: [cpp] while(getline(cin,s)) { if(s=="") break; } [/cpp] 其他的就是常规的字符串处理,map查找输出。具体代码如下: [cpp] #include<iostream> #include<string> #include<map> #include<vector> using namespace std; int main() { string s; map<string,string> mss; vector<string> vs; while(getline(cin,s)) { if(s=="") break; int blankPos=s.find(" ");//找到空格位置 mss[s.substr(blankPos+1)]=s.substr(0,blankPos);//插入map } while(getline(cin,s)) { if(s=="") break; if(mss.find(s)!=mss.end()) { vs.push_back(mss[s]); //cout<<mss[s]<<endl; } else //cout<<"eh"<<endl; vs.push_back("eh"); } int v_size=vs.size(); for(int i=0;i<v_size;i++) { cout<<vs[i]; if(i!=v_size-1) cout<<endl; } return 0; } [/cpp] 本代码提交AC,用时1000MS,内存14648K,效率比较低,如果使用scanf和printf估计会好些。]]>

hihoCoder 1052-基因工程

hihoCoder 1052-基因工程 #1052 : 基因工程 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho正在进行一项基因工程实验。他们要修改一段长度为N的DNA序列,使得这段DNA上最前面的K个碱基组成的序列与最后面的K个碱基组成的序列完全一致。 例如对于序列”ATCGATAC”和K=2,可以通过将第二个碱基修改为”C”使得最前面2个碱基与最后面两个碱基都为”AC”。当然还存在其他修改方法,例如将最后一个碱基改为”T”,或者直接将最前面两个和最后面两个碱基都修改为”GG”。 小Hi和小Ho希望知道在所有方法中,修改碱基最少的方法需要修改多少个碱基。 输入 第一行包含一个整数T(1 <= T <= 10),代表测试数据的数量。 每组测试数据包含2行,第一行是一个由”ATCG”4个大写字母组成的长度为N(1 <= N <= 1000)的字符串。第二行是一个整数K(1 <= K <= N)。 输出 对于每组数据输出最少需要修改的碱基数量。 样例输入 2 ATCGATAC 2 ATACGTCT 6 样例输出 1 3


简单的字符串处理题目,该题要求修改字符保证前k个字符和后k个字符相同,求最小的修改次数。这个题目会让人想到字符串的编辑距离,但不完全相同,因为这题是对单个字符串的修改,不能想当然的把前后k个字符取出来,然后逐一比较是否相等,因为如果前k个字符和后k个字符有重叠的话,你修改了某个字符,会影响下一次比较。 比如字符串CTACATGT,长度n=8,k=6,前k个字符串为s1=CTACAT,后k个字符串为s2=ACATGT,我们不能单独求s1和s2的距离,而是需要把这些字符放到原字符串中,再进一步研究你会发现其实就是在原字符串中保证s[i]=s[i+2],其中2=d=n-k。也就是下图中红色和绿色的两组字符要分别相同。 现在我们分析红色那组字符,首先s[0]==s[2]不成立,可以把s[0]改成A;也可以把s[2]改成C,如果我们把s[2]改成C的话,虽然s[0]==s[2]成立,但是s[2]==s[4]又不成立了,又要修改s[4];但是如果我们把s[0]改成A的话,则s[0]==s[2]==s[4]都成立,且修改次数更少。那么要怎样才能使修改的次数最少呢? 我们回到问题的本源:使用红色那组字符都相同,为了使修改次数最少,我们当然是将所有字符统一成当前出现次数最多的那个字符咯,这样只需要修改较少的非同一字符,在这个例子中,就是修改成出现次数最多的A。 如果前k个字符串和后k个字符串没有重叠,那就很简单了,只需依次检查对应字符是否相等。 理解了以上内容,代码很快就能写出来了: [cpp] #include<iostream> #include<string> using namespace std; //求4个数的最大值 int max4(int a,int b,int c,int d) { int maxab=a>b?a:b; int maxcd=c>d?c:d; return maxab>maxcd?maxab:maxcd; } int main() { int t,k,n,d,ans;//t,k为题意;n为字符串长度;d为n-k相隔为d的字符需要相等;ans表示需要修改的碱基数量。 string s; cin>>t; while(t–) { ans=0; cin>>s>>k; n=s.size(); d=n-k; if(2*k<=n)//如果最前k个碱基和最后k个碱基没有重叠的话 { for(int i=0;i<k;i++) if(s[i]!=s[i+d])//只需要依次检查对应字符是否相等即可 ans++; } else//如果有重叠,则另行讨论 { for(int i=0;i<d;i++)//把所有碱基分成d组,需要确保每组都是同一个字符 { int A=0,T=0,G=0,C=0,num=0; for(int j=i;j<n;j+=d)//对于每一组,求出该组中出现次数最多的碱基的个数 { num++; if(s[j]==’A’) A++; else if(s[j]==’T’) T++; else if(s[j]==’G’) G++; else C++; } int maxATGC=max4(A,T,G,C);//该组全部修改成出现次数最多的碱基 int change_num=num-maxATGC;//用该组中所有碱基个数减去出现次数最多的碱基个数,就是需要修改的碱基个数 ans+=change_num; } } cout<<ans<<endl; } return 0; } [/cpp] 本代码提交AC,用时1MS,内存0MB。 ]]>

hihoCoder 1039-字符消除

hihoCoder 1039-字符消除 #1039 : 字符消除 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi最近在玩一个字符消除游戏。给定一个只包含大写字母”ABC”的字符串s,消除过程是如下进行的: 1)如果s包含长度超过1的由相同字母组成的子串,那么这些子串会被同时消除,余下的子串拼成新的字符串。例如”ABCCBCCCAA”中”CC”,”CCC”和”AA”会被同时消除,余下”AB”和”B”拼成新的字符串”ABB”。 2)上述消除会反复一轮一轮进行,直到新的字符串不包含相邻的相同字符为止。例如”ABCCBCCCAA”经过一轮消除得到”ABB”,再经过一轮消除得到”A” 游戏中的每一关小Hi都会面对一个字符串s。在消除开始前小Hi有机会在s中任意位置(第一个字符之前、最后一个字符之后以及相邻两个字符之间)插入任意一个字符(‘A’,’B’或者’C’),得到字符串t。t经过一系列消除后,小Hi的得分是消除掉的字符的总数。 请帮助小Hi计算要如何插入字符,才能获得最高得分。 输入 输入第一行是一个整数T(1<=T<=100),代表测试数据的数量。 之后T行每行一个由’A”B”C’组成的字符串s,长度不超过100。 输出 对于每一行输入的字符串,输出小Hi最高能得到的分数。 提示 第一组数据:在”ABCBCCCAA”的第2个字符后插入’C’得到”ABCCBCCCAA”,消除后得到”A”,总共消除9个字符(包括插入的’C’)。 第二组数据:”AAA”插入’A’得到”AAAA”,消除后得到””,总共消除4个字符。 第三组数据:无论是插入字符后得到”AABC”,”ABBC”还是”ABCC”都最多消除2个字符。 样例输入 3 ABCBCCCAA AAA ABC 样例输出 9 4 2


这个题目要稍微思考一下,当然很容易想到穷举测试,但我在边写代码的时候还是在担心穷举会不会超时什么的。后来结果还不错。 总体思路就是在字符串的所有可以插空的位置分别插入A、B、C看能消除多少个字符,然后求最大值。所以这里关键就是怎样求一个字符串最终能消除多少个字符。 题目给出的消除策略是这样的:
1)如果s包含长度超过1的由相同字母组成的子串,那么这些子串会被同时消除,余下的子串拼成新的字符串。 2)上述消除会反复一轮一轮进行,直到新的字符串不包含相邻的相同字符为止。
一定要注意,相同的子串同时消除。我一开始想用堆栈来做,来一个字符,判断是否和栈顶相同,如果相同,则消除,并统计消除个数。但是这样会有问题,比如字符串ABCCBCCCAA,如果用堆栈来做的话:进A,进B,进C,C,消除C,C,进B,消除B,B,进C,C,C,消除C,C,C,进A,A,消除A,A,A,这样所有字符都消除了。但是按原题的思路,所有相同的子串同时消除,第一轮同时只能消除C,C,C,C,C,A,A,得到ABB,然后消除B,B,最终剩A。所以用堆栈不行。 后来是这样解决的,只需遍历一遍字符串,如果s[i]==s[i+1]则说明出现重复子串了,令j=i+1,然后一个while(s[i]==s[j])循环计数。如果可以消除,则递归调用该函数继续消除,否则直接返回。 代码如下: [cpp] #include <iostream> #include<string> using namespace std; //递归求解一个字符串最多能消除多少个字符 int get_erase_num(string s,int rs) { int G[100]={0};//用来记录整个字符串能够消除的字符的位置 int s_size=s.size(); bool is_done=true;//是否检查结束的标识 for(int i=0;i<s_size-1;i++)//注意上限s_size-1 { if(s[i]==s[i+1])//然后判断是否有至少两个连续的字符出现 { G[i]=1; is_done=false; rs++; int j=i+1; while(s[i]==s[j]) { G[j]=1; rs++; j++; if(j>=s_size)//检查是否超限 { break; } } i=j-1; } } if(!is_done)//如果可以消除,则还没有结束 { string ss=""; for(int i=0;i<s_size;i++) { if(G[i]==0) ss+=s[i];//同时消除重复子串后的结果串 } return get_erase_num(ss,rs);//递归 } return rs;//结束返回 } int main() { int n; string s; cin>>n; while(n–) { cin>>s; int s_size=s.size(); int max_m=-1; //在所有可以插入的位置依次分别测试插入A,B,C的结果 for(int i=0;i<s_size+1;i++) { if(i==s_size)//如果要在字符串的末尾插入字符 { int A_result=get_erase_num(s+"A",0); if(A_result>max_m) max_m=A_result; int B_result=get_erase_num(s+"B",0); if(B_result>max_m) max_m=B_result; int C_result=get_erase_num(s+"C",0); if(C_result>max_m) max_m=C_result; } else//插入位置除了末尾 { int A_result=get_erase_num(s.substr(0,i)+"A"+s.substr(i),0); if(A_result>max_m) max_m=A_result; int B_result=get_erase_num(s.substr(0,i)+"B"+s.substr(i),0); if(B_result>max_m) max_m=B_result; int C_result=get_erase_num(s.substr(0,i)+"C"+s.substr(i),0); if(C_result>max_m) max_m=C_result; } } cout<<max_m<<endl; } return 0; } [/cpp] 该代码提交结果AC,运行用时45ms,内存0MB,还不错。]]>

hihoCoder 1015-KMP算法

hihoCoder 1015-KMP算法 #1015 : KMP算法 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。 这一天,他们遇到了一只河蟹,于是河蟹就向小Hi和小Ho提出了那个经典的问题:“小Hi和小Ho,你们能不能够判断一段文字(原串)里面是不是存在那么一些……特殊……的文字(模式串)?” 小Hi和小Ho仔细思考了一下,觉得只能想到很简单的做法,但是又觉得既然河蟹先生这么说了,就肯定不会这么容易的让他们回答了,于是他们只能说道:“抱歉,河蟹先生,我们只能想到时间复杂度为(文本长度 * 特殊文字总长度)的方法,即对于每个模式串分开判断,然后依次枚举起始位置并检查是否能够匹配,但是这不是您想要的方法是吧?” 河蟹点了点头,说道:”看来你们的水平还有待提高,这样吧,如果我说只有一个特殊文字,你能不能做到呢?“ 小Ho这时候还有点晕晕乎乎的,但是小Hi很快开口道:”我知道!这就是一个很经典的模式匹配问题!可以使用KMP算法进行求解!“ 河蟹满意的点了点头,对小Hi说道:”既然你知道就好办了,你去把小Ho教会,下周我有重要的任务交给你们!“ ”保证完成任务!”小Hi点头道。 提示一:KMP的思路 提示二:NEXT数组的使用 提示三:如何求解NEXT数组 输入 第一行一个整数N,表示测试数据组数。 接下来的N*2行,每两行表示一个测试数据。在每一个测试数据中,第一行为模式串,由不超过10^4个大写字母组成,第二行为原串,由不超过10^6个大写字母组成。 其中N<=20 输出 对于每一个测试数据,按照它们在输入中出现的顺序输出一行Ans,表示模式串在原串中出现的次数。 样例输入 5 HA HAHAHA WQN WQN ADA ADADADA BABABB BABABABABABABABABB DAD ADDAADAADDAAADAAD 样例输出 3 1 3 1


题目大意就是用KMP算法求解一个子串在主串中出现的次数。我们都知道常规KMP算法可以用来判断一个模式串t是否在主串s中出现过,以及出现的位置。但是这题要求我们求出出现的次数。我们只需要对KMP算法稍加修改即可。 KMP的详细介绍可以看这里,我是仔细看了学校发的李春葆版本《数据结构》P105,代码也基本上照搬课本,使用的是修正后的求nextval数组的KMP算法。具体代码如下: [cpp] #include<iostream> #include<string> using namespace std; int nextval[10001]; //改进的next数组求法 void get_nextval(const string &t) { int j=0,k=-1; nextval[0]=-1; int t_len=t.size(); while(j<t_len) { //t[k]表示前缀,t[j]表示后缀 if(k==-1||t[j]==t[k]) { j++; k++; //较之前next数组求法,改动在下面4行 if(t[j]!=t[k]) nextval[j]=k; //之前只有这一行 else nextval[j]=nextval[k]; // 继续递归 } else k=nextval[k]; // 递归往前找 } } //t为模式串,s为主串;返回t在s中出现的次数 int get_show_times_kmp(const string &t,const string &s) { int t_len=t.size(),s_len=s.size(),i=0,j=0; int times=0; //int *nextval=new int[t_len]; get_nextval(t); while(i<s_len) { if(j==-1||s[i]==t[j]) { i++; j++; } else j=nextval[j]; if(j==t_len) { times++; //i=i-t_len+1; //j=0;//如果匹配成功的话,无需从t的开头重新匹配,也是只要将t右滑j-k个位置。看《数据结构》P107 } } return times; } int main() { int n; string t,s;//t为模式串,s为主串 cin>>n; while(n–) { cin>>t>>s; cout<<get_show_times_kmp(t,s)<<endl; } return 0; } [/cpp] 我做的修改只有一处,就是KMP搜索的时候,如果j==t_len说明在主串s中找到了一个t的匹配,令times++,此时程序还不能结束,我们要让匹配继续下去,为了让while成立,修改while条件只有i<s_len。 其实这个思路是我后来看某大神的解法思考过后才想明白的。我最开始的想法是这样的:既然在t.j和s.i处匹配成功,要进行下一轮匹配时,就应该令j=0,i=i-t_len+1;,也就是从主串s匹配成功起始位置的下一个位置和模式串t的起始位置开始重新匹配。这样做虽然正确,但是TLE超时。后来我仔细看课本P107图4.10,假设$$t=t_0t_j$$ ,且$$t_j==s_i$$,因为$$t_0…t_{k-1}==t_{j-k}…t_{j-1}$$,所以如果要进行下一次匹配,也可以直接将$$t_0…t_{k-1}$$右滑到$$t_{j-k}…t_{j-1}$$的位置,也就是$$t_0…t_{k-1}==s_{i-k}…s_{i-1}$$,再依次判断$$s_i$$是否等于$$t_k$$。这其实就是令j=nextval[j]。所以当j==t_len的时候,不需要特别处理,直接下一次循环就好。这样可以节省很多时间。 以上代码在hiho中AC,用时226ms,内存7MB。]]>