Google Code Jam 2014 Round 1C

B-smallとB-largeを通した(735位)
実装がとても辛かった…
基本的に、1つの種類の文字からなる文字列と2種類以上にまたがっているやつにわけるということをやっている。2種類以上にまたがっているものは、文字を頂点、文字列を辺としたグラフを考えて連結成分の個数を調べ、また、閉路があるかどうかを調べている。
(閉路があったら無理)

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const ll MOD = 1000000007;
int G[26][2];
int O[26],C;
bool used[26];

bool dfs(int v, int d){
	if(used[v]) return false;
	used[v] = true;
	if(G[v][d] == -1) return true;
	return dfs(G[v][d], d);
}
int countC(){ //連結成分の個数を数える
	int ret = 0;
	fill(used,used+26,false);
	for(int i = 0; i < 26; i++){
		if(O[i]>0 && G[i][1]==-1 && G[i][0] == -1){
			ret++;
		}else if(!used[i]){
			int f = 0;
			if(G[i][0] >= 0){
				f=1;
				if(!dfs(i,0)){
					return -1;
				}
			}
			if(G[i][1] >= 0){
				f=1;
				used[i] = false;
				if(!dfs(i,1)){
					return -1;
				}
			}
			ret += f;
		}
	}
	return ret;
}
ll ff[110];
int solve(){
	ll ret = 1;
	for(int i = 0; i < 26; i++){
		if(O[i]>0){
			ret = (ret * ff[O[i]]) % MOD;
		}
	}
	ret = (ret * ff[C]) % MOD;
	return (int)ret;
}
int N;
string S[100];
bool isok(int t){
	vector<P> v;
	string s = S[t];
	int l = s.size();
	for(int i = 0; i < l; i++){
		v.push_back(P(s[i], i));
		if(s[i] != s[0] && s[i] != s[l-1]){
			for(int j=0;j<N;j++){
				if(j==t) continue;
				for(int k=0;k<S[j].size();k++){
					if(S[j][k] == s[i]){
						return false;
					}
				}
			}
		}
	}
	sort(v.begin(), v.end());	
	for(int i=1; i<v.size(); i++){
		if(v[i].first == v[i-1].first){
			if(!(v[i].second == v[i-1].second + 1))
				return false;
		}
	}
	return true;
}
int main(){
	int T;
	scanf("%d", &T);
	ff[0] = 1;
	for(int i = 1; i < 110; i++){
		ff[i] = (ff[i-1] * i) % MOD;
	}
	for(int cnt = 1; cnt <= T; cnt++){
		for(int i=0;i<26;i++){
			G[i][0] = -1;
			G[i][1] = -1;
		}
		fill(O,O+26,0);
		scanf("%d", &N);
		for(int i = 0; i < N; i++){
			char s[1000], c;
			scanf("%c%s",&c,s);
			string str = string(s);
			S[i] = str;
		}
		for(int i = 0; i < N; i++){
			string s = S[i];
			int l = s.size();
			if(s[0] == s[l-1]){
				for(int j = 0; j < l; j++){
					if(s[j] != s[0]) goto zero;
				}
				O[s[0]-'a']++;
			}else{
				if(!isok(i)) goto zero;
				if(G[s[0]-'a'][0] >= 0) goto zero;
				if(G[s[l-1]-'a'][1] >= 0) goto zero;
				G[s[0]-'a'][0] = s[l-1]-'a';
				G[s[l-1]-'a'][1] = s[0]-'a';
			}
		}
		C = countC();
		if(C == -1) goto zero;
		printf("Case #%d: %d\n",cnt, solve());
		continue;

		zero:
		printf("Case #%d: 0\n",cnt);
	}
}