基于哈希与字典树的字符串匹配验证算法
本题要求验证一棵树中从根到任意节点的路径所形成的序列,是否都能在给定的字符串集合中找到匹配项。这里的"匹配"是指该路径序列是某个给定字符串的前缀。
解决方法有两种主要思路:
方案一:字符串哈希 + DFS 验证
首先对输入的 n 个字符串逐个计算其所有前缀的哈希值,并记录在哈希表中。然后构建树结构并以深度优先搜索的方式遍历整棵树,在遍历过程中不断累积当前路径上的哈希值。如果某条路径对应的哈希值未出现在预处理好的哈希表中,则说明不满足条件,直接返回失败结果。
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int MAXN = 2e5 + 7;
const ull BASE = 2333;
unordered_map<ull, bool> hashTable;
int nodeValue[MAXN], head[MAXN], edgeCount;
struct Edge {
int to, next;
} edges[MAXN * 2];
void addEdge(int u, int v) {
edges[edgeCount] = {v, head[u]};
head[u] = edgeCount++;
edges[edgeCount] = {u, head[v]};
head[v] = edgeCount++;
}
bool dfsCheck(int currentNode, int parent, ull currentHash) {
for (int i = head[currentNode]; ~i; i = edges[i].next) {
int nextNode = edges[i].to;
if (nextNode == parent) continue;
ull newHash = currentHash * BASE + nodeValue[nextNode];
if (!hashTable[newHash]) return false;
if (!dfsCheck(nextNode, currentNode, newHash)) return false;
}
return true;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
memset(head, -1, sizeof(head));
ull prefixHash;
int length, value;
for (int i = 0; i < n; ++i) {
scanf("%d", &length);
prefixHash = 0;
for (int j = 0; j < length; ++j) {
scanf("%d", &value);
prefixHash = prefixHash * BASE + value;
hashTable[prefixHash] = true;
}
}
for (int i = 1; i <= m; ++i) {
scanf("%d", &nodeValue[i]);
}
int u, v;
for (int i = 1; i < m; ++i) {
scanf("%d%d", &u, &v);
addEdge(u, v);
}
ull rootHash = nodeValue[1];
if (!hashTable[rootHash]) {
printf("NO\n");
} else {
if (dfsCheck(1, 0, rootHash)) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}
方案二:字典树(Trie)实现方式
另一种更直观的做法是使用字典树来存储所有目标字符串的前缀信息。对于每一个输入的字符串,将其所有字符依次插入到字典树中。之后再通过DFS遍历整棵树,每一步都检查当前节点的值是否存在字典树中的对应子节点,若不存在则表示无法匹配。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 7;
int nodeValues[MAXN];
vector<int> tree[MAXN];
map<int, int> trieNodes[MAXN];
int nodeCount = 1;
void insertString(const vector<int>& str) {
int curNode = 0;
for (int ch : str) {
if (trieNodes[curNode].find(ch) == trieNodes[curNode].end()) {
trieNodes[nodeCount].clear();
trieNodes[curNode][ch] = nodeCount++;
}
curNode = trieNodes[curNode][ch];
}
}
bool traverseTree(int curTreeNode, int parent, int curTrieNode) {
bool result = true;
for (int child : tree[curTreeNode]) {
if (child == parent) continue;
auto it = trieNodes[curTrieNode].find(nodeValues[child]);
if (it == trieNodes[curTrieNode].end()) return false;
result &= traverseTree(child, curTreeNode, it->second);
}
return result;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
vector<int> tempStr;
int len, val;
for (int i = 0; i < n; ++i) {
scanf("%d", &len);
tempStr.clear();
for (int j = 0; j < len; ++j) {
scanf("%d", &val);
tempStr.push_back(val);
}
insertString(tempStr);
}
for (int i = 1; i <= m; ++i) {
scanf("%d", &nodeValues[i]);
}
int x, y;
for (int i = 1; i < m; ++i) {
scanf("%d%d", &x, &y);
tree[x].push_back(y);
tree[y].push_back(x);
}
if (trieNodes[0].find(nodeValues[1]) == trieNodes[0].end()) {
printf("NO\n");
} else {
int startTrieNode = trieNodes[0][nodeValues[1]];
if (traverseTree(1, 0, startTrieNode)) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}