题目链接:https://codeforces.com/gym/102770/problem/C
题意
给你一个N x N的矩阵,矩阵由小写字母和#组成,#为障碍。然后给出m个字符串和该字符串对应的值。然后从矩阵中截取子串,子串指矩阵中水平方向从左到右碰到障碍的最长串,或者竖直方向从上到下碰到障碍的最长串,也或是碰到矩阵边界的最长串,问这些子串是否都存在于m个给定的字符串中,若存在则输出值的总和,否则输出-1。
思路
- 现根据m个字符串创建字典树,数组w存储字符串对应的值。
- 接着就是遍历矩阵截取字符串,每截取一个字符串就去字典树中查找,若为查到直接退出输出-1,若查到就累加答案。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
const int maxm=4e6+10;
int n,m,tot;
int tree[maxm][26],w[maxm];
char s[maxn][maxn],str[maxn];
//因为有多组测试数据,每次只需对tot个结点进行初始化
void init()
{
for(int i=0;i<=tot;i++){
for(int j=0;j<26;j++)
tree[i][j]=0;
w[i]=0;
}
tot=0;
}
void Insert(char* str,int x)
{
int root=0;
int len=strlen(str);
for(int i=0;i<len;i++){
int id=str[i]-'a';
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
}
w[root]=x;
}
int Find(char* str)
{
int root=0;
int len=strlen(str);
for(int i=0;i<len;i++){
int id=str[i]-'a';
if(!tree[root][id]) return -1;
root=tree[root][id];
}
//如果w[root]==0,则表示str串不在字典树中
if(w[root]) return w[root];
else return -1;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
init();
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
while(m--){
int x;
scanf("%s %d",str,&x);
Insert(str,x);
}
ll ans=0;
bool flag=true;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int sz=0;
if(s[i][j]=='#') continue;
//遍历当前行的字符串
if(i==1||s[i-1][j]=='#'){
int x=i;
while(x<=n&&s[x][j]!='#'){
str[sz++]=s[x][j];
x++;
}
str[sz]='\0';
int v=Find(str);
if(v==-1){
flag=false;
break;
}
ans+=v;
}
//遍历当前列的字符串
sz=0;
if(j==1||s[i][j-1]=='#'){
int y=j;
while(y<=n&&s[i][y]!='#'){
str[sz++]=s[i][y];
y++;
}
str[sz]='\0';
int v=Find(str);
if(v==-1){
flag=false;
break;
}
ans+=v;
}
}
if(!flag) break;
}
if(flag) printf("%lld\n",ans);
else puts("-1");
}
return 0;
}