原文链接(http://blog.csdn.net/mottolinux/article/details/48502435)
A题 Amusing Digits
问题描述
给定一个数串,从中选出尽可能多的“9706”子串,要求每个digit的相对先后顺序不变且每个digit只能用一次,问最多可以选出多少个9706子串。
样例输入
4
6097
97069706
997776600069
123901370997606
样例输出
0
2
1
2
解题思路
通过分析,本题满足使用贪心策略。考虑数串9799706,若选择数字9时,跳过第一个9选择第二个9,将会导致第一个数字7无法匹配,因此每次选择数字9时,优先最靠前没使用过的,可以达到最优。同理其他数字也满足该原则。
根据以上分析,容易得到思路:遍历整个数串,选取最靠前的9;若找到9,再选取9以后最靠前的7;依次同理把0和6找到。但很可惜,这样的解法是要TLE的,该方法的复杂度为O((k+1)*n),k为答案,n为(去掉其他无关数字后的)数串长度。构造输入99..977..700..066..6,每个数字都有10000个,这样程序就gg了。
换一种思路,考虑到每次遍历都是要选取最靠前的数字,而下一个候选数字又必须在前一个数字的后面。只考虑数字9和数字7,有效子串的个数实际上取决于有多少个7,每个7的前面都恰好有一个9和它对应。把数字0和6考虑进去同理,所以最终有效子串的个数,取决于有多少个6,每个6的前面都恰有一个0和它对应;这些0中的每一个,前面都恰有一个7和他对应……时间复杂度为O(n)。
#include <iostream>
#include <string>
using namespace std;
int main()
{
int T;
cin >> T;
while (T--)
{
string s;
cin >> s;
int sz = s.length();
int c9, c7, c0, c6;
c9 = c7 = c0 = c6 = 0;
for (int i = 0; i < sz; i++)
{
switch(s[i])
{
case '9': c9++; break;
case '7': if (c7 < c9) c7++; break;
case '0': if (c0 < c7) c0++; break;
case '6': if (c6 < c0) c6++; break;
}
}
cout << c6 << endl;
}
return 0;
}
B题 Best Compression Algorithms
问题描述
给定一个字符串,仅有圆括号,大写字母和数字组成。字串的数字表示它前面的字母或者括号中的模式的个数,如A2表示AA,(BC)3表示BCBCBC;允许复合,如(A3(BC)2)2表示AAABCBCAAABCBC。求给定字符串展开后的长度(即不包含数字和括号的表示的长度)。
样例输入
4
(AA)2A
((A2B)2)2G
WANGYI
A2BC4D2
样例输出
5
13
6
9
解题思路
该题的嵌套括号,可以联想到使用栈来保存每一层级的字符个数缓存。考虑变量k为某一层括号中的临时字符个数统计,初始为0,若遇到非括号嵌套的字符直接累加;若遇到左括号,将当前k的值压入栈,对k置0,统计下一层括号中字符个数;若遇到右括号,则将栈顶元素弹出(表示上一层字符个数的缓存),累加到k。
#include <iostream>
#include <stack>
#include <string>
using namespace std;
inline bool isdigit(char c)
{
return c >= '0' && c <= '9';
}
inline bool isalpha(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
int main()
{
int T;
cin >> T;
while (T--)
{
long long res = 0;
string s;
cin >> s;
int sz = s.length();
long long i = 0, k = 0;
stack<long long> st;
while (i < sz)
{
if (s[i] == '(')
{
st.push(k);
k = 0;
i++;
}
else if (s[i] == ')')
{
i++;
long long count = 0;
while (i < sz && isdigit(s[i]))
{
count = count * 10 + s[i] - '0';
i++;
}
long long t = st.top(); st.pop();
k = k * count + t;
}
else if (isalpha(s[i]))
{
if (i < sz-1 && isdigit(s[i+1]))
{
i++;
long long count = 0;
while (i < sz && isdigit(s[i]))
{
count = count * 10 + s[i] - '0';
i++;
}
k += count;
}
else
{
k += 1;
i++;
}
}
}
cout << k << endl;
}
return 0;
}
C题 Complicated Expression
问题描述
编写一个简易的Lisp解释器。只包含三种运算符+ - *,操作数均为非负整数组成。
语法:每个计算表达式必须包含在一对圆括号内,而且由一个运算符和至少一个操作数组成,格式为(运算符 操作数1 操作数2 ...),允许嵌套,即操作数本身也可以是计算表达式。如:
(+ 9) 表示正整数9;
(+ 1 2) 代表 1 + 2;
(+ 1 2 3 4) 代表 1, 2, 3, 4 的连加和。
(- 9) 表示整数-9;
(- 5 4) 代表 5 - 4。
(* 2 3) 代表 2 * 3;
(* 1 2 3 4) 代表 1 * 2 * 3 * 4。
(+ (* 2 3) 1) 也是一个合法的表达式,代表了 2 * 3 + 1
注意,-运算不能有超过2个操作数;*运算不能只有1个操作数。
运算符、圆括号、操作数之间,在不引起歧义的前提下,允许有任意个空格(包括0个)。
输入一个表达式,首先判断它是否合法的,若不合法输出invalid expression 否则输出表达式的值。
样例输入
5
(+ 1 (* 2 3)))
(2 3)
(- 3 2 1)
(+ (+ 1 2) (* 2 3) (- 2 1))
(- 2)
样例输出
invalid expression
invalid expression
invalid expression
10
-2
解题思路
本题可以直接根据Lisp的“代换规则(substitution rule)”为思路,递归求值表达式:将表达式中的运算符、操作数依次压入栈,若遇到操作数为一个子表达式,先递归计算子表达式,返回值再压入栈,最后对栈中的运算符、操作数进行计算。注意判断哪些情况为非法表达式。先考虑整个表达式的括号是否匹配,对后面的操作会方便一些。
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <stack>
using namespace std;
char s_cstr[1000006];
inline bool isdigit(char c)
{
return c >= '0' && c <= '9';
}
long long eval(string & s, bool & is_invalid)
{
int sz = s.length();
stack<long long> st;
int i = 0;
int ops = -1;
if (is_invalid || sz == 0) goto fail_exit;
while (i < sz)
{
if (s[i] == '+' || s[i] == '-' || s[i] == '*')
{
if (!st.empty()) goto fail_exit;
switch(s[i])
{
case '+': st.push(0); ops = 0; break;
case '-': st.push(1); ops = 1; break;
case '*': st.push(2); ops = 2; break;
}
i++;
}
else if (isdigit(s[i]))
{
if (st.empty()) goto fail_exit;
long long count = 0;
do
{
count = count * 10 + s[i++] - '0';
} while (i < sz && isdigit(s[i]));
st.push(count);
}
else if (s[i] == '(')
{
string s1;
i++;
int m = 0;
while (i < sz && m >= 0)
{
if (s[i] == '(') m++;
else if (s[i] == ')')
{
if (m > 0) m--;
else break;
}
s1 += s[i++];
}
long long res = eval(s1, is_invalid);
if (is_invalid) return -1;
st.push(res);
i++;
}
else i++;
}
if (st.size() < 2 || ops == -1) goto fail_exit;
else if (ops == 1 && st.size() > 3) goto fail_exit;
else if (ops == 2 && st.size() < 3) goto fail_exit;
if (ops == 0)
{
long long res = 0;
while (!st.empty())
{
res += st.top();
st.pop();
}
return res;
}
if (ops == 1)
{
if (st.size() == 2)
{
return -st.top();
}
else
{
long long i1 = st.top(); st.pop();
long long i2 = st.top(); st.pop();
return i2 - i1;
}
}
if (ops == 2)
{
long long res = 1;
while (st.size() > 1)
{
res *= st.top();
st.pop();
}
return res;
}
fail_exit:
is_invalid = true;
return -1;
}
int main()
{
int T;
cin >> T;
fgets(s_cstr, 1000000, stdin);
while (T--)
{
fgets(s_cstr, 1000000, stdin);
string s(s_cstr, 0, strlen(s_cstr)-1);
int sz = s.length();
if (!(s[0] == '(' && s[sz-1] == ')'))
{
cout << "invalid expression\n";
continue;
}
stack<int> st;
bool is_invalid = false;
for (int i = 0; i < sz; i++)
{
if (s[i] == '(') st.push(i);
else if(s[i] == ')')
{
if (st.empty())
{
is_invalid = true;
break;
}
int j = st.top(); st.pop();
}
}
if (is_invalid || !st.empty())
{
cout << "invalid expression\n";
continue;
}
string s1(s, 1, sz-2);
int res = eval(s1, is_invalid);
if (is_invalid)
{
cout << "invalid expression\n";
continue;
}
else
{
cout << res << endl;
}
}
}