1
30
2013
8

Facebook Hacker Cup 2013 Qualification Round

Beautiful strings
20 points

When John was a little kid he didn't have much to do. There was no internet, no Facebook, and no programs to hack on. So he did the only thing he could... he evaluated the beauty of strings in a quest to discover the most beautiful string in the world.

Given a string s, little Johnny defined the beauty of the string as the sum of the beauty of the letters in it.

The beauty of each letter is an integer between 1 and 26, inclusive, and no two letters have the same beauty. Johnny doesn't care about whether letters are uppercase or lowercase, so that doesn't affect the beauty of a letter. (Uppercase 'F' is exactly as beautiful as lowercase 'f', for example.)

You're a student writing a report on the youth of this famous hacker. You found the string that Johnny considered most beautiful. What is the maximum possible beauty of this string?

Input

The input file consists of a single integer m followed by m lines.

Output

Your output should consist of, for each test case, a line containing the string "Case #x: y" where x is the case number (with 1 being the first case in the input file, 2 being the second, etc.) and y is the maximum beauty for that test case.

Constraints

5 ≤ m ≤ 50
2 ≤ length of s ≤ 500

贪心地把最大beauty给出现频率最大的letter即可。

#include<iostream>
#include<fstream>
#include<string.h>
#include<algorithm>
using namespace std;
//char s[501];
string s;
int m;
ifstream fin;
ofstream fout;
int main(){
	fin.open("beautiful_stringstxt.txt");
	fout.open("output.txt");
	fin>>m;
	getline(fin,s);
	for(int cas=1;cas<=m;cas++){
		getline(fin,s);
		//cout<<s<<endl;
		int len=s.size();
		int count[26];
		for(int i=0;i<26;i++)count[i]=0;
		for(int i=0;i<len;i++){
			char c=s[i];
			if(c<='Z'&&c>='A')c=c-'A'+'a';
			if(c<='z'&&c>='a'){
				count[c-'a']++;
			}
		}
		sort(count,count+26);
		int ans=0;
		for(int i=26;i>=1;i--){
			ans+=i*count[i-1];
		}
		fout<<"Case #"<<cas<<": "<<ans<<endl;
	}
	return 0;
}

Balanced Smileys
35 points

Your friend John uses a lot of emoticons when you talk to him on Messenger. In addition to being a person who likes to express himself through emoticons, he hates unbalanced parenthesis so much that it makes him go :(

Sometimes he puts emoticons within parentheses, and you find it hard to tell if a parenthesis really is a parenthesis or part of an emoticon.

A message has balanced parentheses if it consists of one of the following:

  • - An empty string ""
  • - One or more of the following characters: 'a' to 'z', ' ' (a space) or ':' (a colon)
  • - An open parenthesis '(', followed by a message with balanced parentheses, followed by a close parenthesis ')'.
  • - A message with balanced parentheses followed by another message with balanced parentheses.
  • - A smiley face ":)" or a frowny face ":("

Write a program that determines if there is a way to interpret his message while leaving the parentheses balanced.

Input

The first line of the input contains a number T (1 ≤ T ≤ 50), the number of test cases.
The following T lines each contain a message of length s that you got from John.

Output

For each of the test cases numbered in order from 1 to T, output "Case #i: " followed by a string stating whether or not it is possible that the message had balanced parentheses. If it is, the string should be "YES", else it should be "NO" (all quotes for clarity only)

Constraints

  • 1 ≤ length of s ≤ 100

我写了一个O(n3)的DP,思路正确结果WA了,原因是忽略了“()”这种边界情况:

#include<iostream>
#include<string>
using namespace std;

int T;
string s;
ifstream fin;
ofstream fout;
bool dp[101][101];
int main(){
	fin.open("balanced_smileystxt.txt");
	fout.open("output.txt");
	fin>>T;
	getline(fin,s);
	for(int cas=1;cas<=T;cas++){
		for(int i=0;i<101;i++)for(int j=0;j<101;j++)dp[i][j]=false;
		getline(fin,s);
		int len=s.size();
		int k;
		for(k=0;k<len;k++){
			char c=s[k];
			if(c!=' '&&(c<'a'||c>'z')&&c!=':'&&c!='('&&c!=')'){
				break;
			}
		}
		if(k<len){
			fout<<"Case #"<<cas<<": NO"<<endl;
			continue;
		}
		if(len==0){
			fout<<"Case #"<<cas<<": YES"<<endl;
			continue;
		}
		for(int l=1;l<=len;l++){
			for(int i=0;i+l-1<len;i++){
				if(l==1){
					if(s[i]!='('&&s[i]!=')')dp[i][i]=true;
					else dp[i][i]=false;
					continue;
				}
				else if(l==2){//忽略了“()”的情况!!
					if((s[i]==':'&&s[i+1]==')')||(s[i]==':'&&s[i+1]=='(')){
						dp[i][i+1]=true;
					}
				}
				for(int k=i;k<i+l-1;k++){
					if(dp[i][k]&&dp[k+1][i+l-1]){
						dp[i][i+l-1]=true;
						break;
					}
				}
				if(l>=3){
					if(s[i]=='('&&s[i+l-1]==')'&&dp[i+1][i+l-2]){
						dp[i][i+l-1]=true;
					}
				}
			}
		}
		fout<<"Case #"<<cas<<": ";
		if(dp[0][len-1])fout<<"YES";
		else fout<<"NO";
		fout<<endl;
	}
	return 0;
}

官方题解给出了一个O(n)的答案,实际上这里只需要检测括号是否有可能匹配。


Find the Min
45 points
 

After sending smileys, John decided to play with arrays. Did you know that hackers enjoy playing with arrays? John has a zero-based index array, m, which contains n non-negative integers. However, only the first k values of the array are known to him, and he wants to figure out the rest.

John knows the following: for each index i, where k <= i < n, m[i] is the minimum non-negative integer which is *not* contained in the previous *k* values of m.

For example, if k = 3, n = 4 and the known values of m are [2, 3, 0], he can figure out that m[3] = 1.

John is very busy making the world more open and connected, as such, he doesn't have time to figure out the rest of the array. It is your task to help him.

Given the first k values of m, calculate the nth value of this array. (i.e. m[n - 1]).

Because the values of n and k can be very large, we use a pseudo-random number generator to calculate the first k values of m. Given non-negative integers a, b, c and positive integer r, the known values of m can be calculated as follows:

  • m[0] = a
  • m[i] = (b * m[i - 1] + c) % r, 0 < i < k

Input

The first line contains an integer T (T <= 20), the number of test cases.
This is followed by T test cases, consisting of 2 lines each.
The first line of each test case contains 2 space separated integers, n, k (1 <= k <= 105, k < n <= 109).
The second line of each test case contains 4 space separated integers a, b, c, r (0 <= a, b, c <= 109, 1 <= r <= 109).

Output

For each test case, output a single line containing the case number and the nth element of m.

注意到序列是循环的这个问题就很好解决了,只需要求出第一个“循环节”。这时候一个暴力的方法是O(k2)的,仍然难以接受,为此我加入了一个小优化:

考虑a,x1,...,xk,y这个序列片段,现在我们要求y,如果a<xk并且a在x1~xk-1 中没有再次出现,则y=a;否则我们只需从小到大检验大于xk的数。这样在最坏情况下也可以秒杀。

#include<iostream>
#include<fstream>

using namespace std;
int had[100001];
int circle[100001];
int memo[100001];
int T;
long long n,k;
long long a,b,c,r;
ifstream fin;
ofstream fout;

int main(){
    fin.open("find_the_mintxt.txt");
    fout.open("output.txt");
    fin>>T;
    for(int cas=1;cas<=T;cas++){
        fin>>n>>k;
        fin>>a>>b>>c>>r;
        
        for(int i=0;i<=k;i++)had[i]=0;
        
        long long pre=a;
        if(a<=k)had[a]++;
        memo[0]=a;
        for(int i=1;i<k;i++){
            pre=(b*pre+c)%r;
            if(pre<=k)had[pre]++;
            memo[i]=pre;
            //fout<<memo[i]<<' ';
        }
        //fout<<endl;
        int j;
        for(j=0;j<=k;j++){
            if(had[j]==0)break;
        }
        circle[0]=j;
        if(memo[0]<=k)had[memo[0]]--;
        had[j]++;
        //fout<<circle[0]<<' ';
        
        for(int i=1;i<=k;i++){
            
            if(memo[i-1]<circle[i-1]&&had[memo[i-1]]==0){
                circle[i]=memo[i-1];
            }
            else{
                int x;
                for(x=circle[i-1]+1;x<=k;x++){
                    if(had[x]==0)break;
                }
                circle[i]=x;
            }
            
            
            /*
            int j;
            for(j=0;j<=k;j++){
                if(had[j]==0)break;
            }
            circle[i]=j;
            */
            
            if(i<k){
                if(memo[i]<=k)had[memo[i]]--;
                had[circle[i]]++;
            }
            //fout<<circle[i]<<' ';
        }
        //fout<<endl;
        fout<<"Case #"<<cas<<": "<<circle[(n-k-1)%(k+1)]<<endl;
    }
    return 0;
}

官方题解:

The qualification round is over, and 10169 hackers solved at least one problem. The most exciting things to watch in this round was who finished first, and who would get the last passing submission. There was only one person with a total penalty of less than an hour, which made Mark the clear winner. A few people were playing a game of chicken to see who would get the last submission. The winner of this game submitted his solution to Beautiful Strings with only 4 seconds left in the round, to claim the honor of finishing last among all qualifiers. Congratulations to Ryan for this accomplishment! Reminder: the strategy of finishing last might not work as well for future rounds...

Beautiful Strings

This was the easiest problem in the round. It was attempted by 10697 contestants, and solved by 9865. The main idea is to count the frequency of each letter, then assign the value 26 to the most frequent letter, 25 to the next, etc. If two letters are tied for most frequent, it doesn't matter which of them gets which value, since the sum will be the same. The python code below explains the solution pretty well.

from collections import Counter

def get_beauty(string):

    string = string.lower()

    # Remove all characters other than letters

    string = ''.join(x for x in string if 'a' <= x <= 'z' )

    # Make a dictionary where the keys are letters and the values are counts

    freq = Counter(string)

    # Get the values (letter counts) and sort them in descending order

    arr = freq.values()

    arr.sort()

    arr.reverse()

    # 26 * (count of most common letter) + (25 * next most common) + ...

    values_and_counts = zip(range(26, 0, -1), arr)

    return sum(value * count for value, count in values_and_counts)

Balanced Smileys

This problem was attempted by 7096 contestants, but only 2860 solved it. There are a lot of ways to solve this problem. You could go for the brute force solution, which was O(2^N), a dynamic programming/memoization approach, which would be O(N^2)/O(N^3), or the solution intended by the writer, which was O(N). We decided to let everyone who made a correct solution pass, so any of the above actually passes our tests. The number of passing submissions for this problem is just what we wanted from the qualification round, so we think it was a good call. This post will only cover the O(N) solution.

The idea is to keep track of the possible range of open parentheses.

We use two values, 'minOpen' and 'maxOpen'. Initialize both of these to 0.

Iterate over the message, character by character.

Whenever you encounter a '(', you increment maxOpen, and if it wasn't part of a smiley, you also increment minOpen.

Whenever you encounter a ')', you decrement minOpen, and if it wasn't part of a frowny face, decrement maxOpen. If minOpen is negative, reset it to 0.

If maxOpen ever was negative, or minOpen isn't 0, it wasn't possible that the message had balanced parentheses. Otherwise it was possible. Python code that solves this problem is below.

def isBalanced(message):

    minOpen = 0

    maxOpen = 0

    for i in xrange(len(message)):

        if message[i] == '(':

            maxOpen += 1

            if i != 0 and message[i-1] != ':':

                minOpen += 1

        elif message[i] == ')':

            minOpen = max(0, minOpen-1)

            if i != 0 and message[i-1] != ':':

                maxOpen -= 1

                if maxOpen < 0:

                    break

    if maxOpen >= 0 and minOpen == 0:

        return "YES"

    else:

        return "NO"

Find the Min

This problem was attempted by 2555 contestants, and solved by 1929, making it the hardest problem in this round. The most challenging part of this problem is the large n. To solve this problem, you should notice 2 things:

  1. The maximum result of m[i] (i >= k) will not be larger than k+1, so even though m[i] (i < k) may be as large as 10^9, the solution is never larger than k + 1
  2. The result of m will be repeated every k+1 numbers. This means that we just have to calculate the k+1 next numbers, even if n is very large.

Now we've reduced the problem to the following: find m[k], m[k+1], ..., m[2k+1]. The brute force version of doing this is still too slow (O(k^2)), so we have to use a BST (set/map in C++) to maintain the 'available' values, i.e the values not include in previous k elements. This reduces the time complexity to O(k log k), which should run well within the 6 minutes. For a clean implementation of this idea, take a look at the solution of Mark in first place.

//这里用到了set和multiset来维护数集
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <map>
#include <set>
#include <queue>

using namespace std;

int M[200020];

int main() {
  int N; cin >> N;
  for(int t = 0; t < N; t++) {
    int n, k; cin >> n >> k; n--;
    int a, b, c, r; cin >> a >> b >> c >> r;

    M[0] = a;
    for(int i = 1; i < k; i++) {
      M[i] = (1ll * b * M[i - 1] + c) % r;
    }

    set<int> st;
    for(int i = 0; i <= k; i++) st.insert(i);
    for(int i = 0; i < k; i++) st.erase(M[i]);

    multiset<int> dupst;
    for(int i = 0; i < k; i++) dupst.insert(M[i]);

    for(int i = k; i <= 2 * k; i++) {
      M[i] = *st.begin();
      st.erase(st.begin());

      if(i < 2 * k) {
        dupst.erase(dupst.find(M[i - k]));
        if(M[i - k] <= k && dupst.find(M[i - k]) == dupst.end()) {
          st.insert(M[i - k]);
        }
      }
    }

    cout << "Case #" << (t + 1) << ": ";
    if(n <= 2 * k) {
      cout << M[n] << endl;
    } else {
      cout << M[k + (n - 2 * k - 1) % (k + 1)] << endl;
    }
  }
  return 0;
}

Take a look at people's solutions from https://www.facebook.com/hackercup/scoreboard?round=185564241586420 to get some more pointers on how to solve the problems.

Good luck to everyone that qualified to Round 1!

Writers:

Beautiful Strings: David Alves

Balanced Smileys: Torbjørn Morland

Find the Min: ZiHing Cheung

 

Category: HackerCup | Tags: set multiset dp

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com