C. Crossword Validation
A crossword is a word puzzle that usually takes the form of a square or a rectangular grid of white- and black-shaded cells. The game's goal is to fill the white cells with letters, forming words or phrases, by solving clues, which lead to the answers. The answer words and phrases are typically placed in the grid from left to right and from top to bottom. The shaded cells are used to separate the words or phrases. Crossword grids such as those appearing in most North American newspapers and magazines feature solid areas of white cells. Every letter is checked (i.e. is part of both an "across" word and a "down" word) and usually each answer must contain at least three letters. In such puzzles, shaded cells are typically limited to about one-sixth of the total. Crossword grids elsewhere, such as in Britain, South Africa, India and Australia, have a lattice-like structure, with a higher percentage of shaded cells, leaving about half the letters in an answer unchecked. For example, if the top row has an answer running all the way across, there will often be no across answers in the second row.
Crossword puzzles have a long history. The title for the world's first crossword puzzle is disputed, but most people believe the crossword puzzle is invented in the 19th century. With the emergence of personal computers, more and more crossword puzzles are distributed online instead of on newspapers and books. Software that aids in creating and solving crossword puzzles has been written since at least 1976. In this problem, you are to write a program that checks if a solution for a crossword puzzle is valid. Note that the rules of the puzzle in this problem, which is described in the next paragraph, may differ from the real-world ones.
You are given a completed crossword puzzle on a $N \times N$ grid. Each cell is either a white cell filled with a letter or a black cell. You are also given a dictionary of $M$ distinct words, where each word has a score associated with it. A horizontal candidate word in the grid is a continuous string of letters on the same row of the grid that can't be extended. Formally, this means that the cell to the left of the leftmost cell of the candidate word either doesn't exist or is a black cell, and the same goes for the cell to the right of the rightmost cell. Vertical candidate words are defined similarly, except that the letters are on the same column. Candidate words are read from left to right for horizontal words or from top to bottom for vertical words. The crossword puzzle is valid if and only if each candidate word is present in the given dictionary. The score of a valid puzzle is the sum of the scores in the dictionary associated with each candidate word. Note that two or more candidate words may be the same. In this case, the score of that word is added multiple times accordingly. Your program must determine the score of the solution, or report that it is not valid.
**Input**
The input contains multiple cases. The first line of the input contains a single positive integer $T$, the number of cases.
For each case, the first line of the input contains two integers $N, M \ (1 \le N \le 1\,000, 1 \le M \le 4\times 10^6)$, the size of the grid and the number of words in the dictionary. The following $N$ lines each contain a string of length $N$, where each character is either a lowercase English letter or '#'. If the $j$\-th character of the $i$\-th string is '#', then the cell located in the intersection of the $i$\-th row and the $j$\-th column is a black cell. Otherwise, this character denotes the letter filled in that cell. The following $M$ lines each contain a non-empty string consisting of only lowercase English letters, and a positive integer, denoting a word in the dictionary and its score. It is guaranteed that those $M$ words are pairwise distinct, the length of each word doesn't exceed $N$, and the score of each word doesn't exceed $10^9$.
It is guaranteed that the sum of $N^2$ over all cases doesn't exceed $4\times 10^6$, and the sum of the lengths of all words in the dictionary over all cases doesn't exceed $4\times 10^6$.
**Output**
For each case, print a single integer in a single line, the score of the crossword solution given in the input if it is valid. If the solution is not valid, print $-1$ instead.
Example
Input
2
2 4
ab
#d
ab 1
a 2
d 3
bd 4
2 4
ab
c#
ab 5
ca 2
b 6
c 7
Output
10 -1
题目来源:The 17th Zhejiang Provincial Collegiate Programming Contest
偶遇省赛c题,题干长度堪比阅读理解,拼尽全力无法战胜。
大致意思就是一个Crossword Validation(填字游戏验证),给定一个 N×N 的填字游戏网格和一个字典,每个字典里的单词都有一个权值,需验证网格中的所有从上到下和从左到右的字符串是否均存在于字典中,字符串以‘#’符号或边界分隔。
若全部有效,计算所有候选词的权值总和;否则输出 - 1。字典中的单词可能重复出现,按出现次数累加分数 。
对于字符串的统计我们很容易想到字典树,当然这题用unordered_map存也是可以的,这里给出字典树的解法(摸了)。
输入数据组数t,每组数据第一行是网格大小n和单词数m,后面n行长度为n的由小写字母和‘#’组成的字符串,以及m行单词和对应的权值
和传统的字典树统计以每个节点结尾的单词插入的次数不同,我们需要用每个单词最后那个节点(cnt[u])记录每个单词的权值
这里用insert函数插入字典中每个单词s及其权值val
int ch[N][30],cnt[N],idx=0;
map mp;
void init(){
ll idx=0;
for (char c='a';c<='z';c++) mp[c]=++idx;
}
void insert(string s,int val) {
int u=0;
for (char &c:s){
int v=mp[c];
if (!ch[u][v]) ch[u][v]=++idx;
u=ch[u][v];
}
cnt[u]=val;
}
对于网格里的字符串,通过query函数查询其是否在字典中,即是否已经插入字典树
由于这里采用的是stringstream流以‘#’分隔字符串的方式,所以特判了一下字符串为空的情况
flag负责判断字符串是否合法,一旦无法找到对应的单词(!ch[u][v])或者字符串仅仅是单词的前缀(cnt[u]==0),就将flag判为false
ll query(string s){
if (s.empty()) return 0;
int u=0;
for (char &c:s){
int v=mp[c];
if (!ch[u][v]){
flag=false;
return 0;
}
u=ch[u][v];
}
if (cnt[u]==0) flag=false;
return cnt[u];
}
需要注意每组数据开头都应清空字典树,我们只需用clear函数初始化已使用的前idx个节点为0即可
void clear() {
for (int i=0;i<=idx;i++){
cnt[i]=0;
memset(ch[i],0,sizeof ch[i]);
}
idx=0;
}
我们可以将上述内容封装为Trie结构体,方便后续调用
最后附上完整代码
#include
using namespace std;
const int N=4e6+5;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using ll=long long;
int t,n,m,val;
char c[1005][1005];
string s;
bool flag;
struct Trie {
int ch[N][30],cnt[N],idx=0;
map mp;
void init(){
ll idx=0;
for (char c='a';c<='z';c++) mp[c]=++idx;
}
void insert(string s,int val) {
int u=0;
for (char &c:s){
int v=mp[c];
if (!ch[u][v]) ch[u][v]=++idx;
u=ch[u][v];
}
cnt[u]=val;
}
ll query(string s){
if (s.empty()) return 0;
int u=0;
for (char &c:s){
int v=mp[c];
if (!ch[u][v]){
flag=false;
return 0;
}
u=ch[u][v];
}
if (cnt[u]==0) flag=false;
return cnt[u];
}
void clear() {
for (int i=0;i<=idx;i++){
cnt[i]=0;
memset(ch[i],0,sizeof ch[i]);
}
idx=0;
}
}trie;
int main() {
IOS;
cin >> t;
trie.init();
while (t--){
ll ans=0;
flag=true;
cin >> n >> m;
trie.clear();
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
cin >> c[i][j];
while (m--){
cin >> s >> val;
trie.insert(s,val);
}
for (int i=1;i<=n;i++){
string str;
for (int j=1;j<=n;j++)
str+=c[i][j];
stringstream ss(str);
while (getline(ss,s,'#'))
ans+=trie.query(s);
}
for (int i=1;i<=n;i++){
string str;
for (int j=1;j<=n;j++)
str+=c[j][i];
stringstream ss(str);
while (getline(ss,s,'#'))
ans+=trie.query(s);
}
cout << (flag?ans:-1) << endl;
}
}