2018年世界杯期间,冰岛队以1:1战平阿根廷队的出色表现引起广泛关注。有趣的是,人们发现冰岛人的姓氏系统与大多数国家不同,这背后隐藏着一个独特的家族关系计算问题。
冰岛采用维京人古老的父系姓氏制度:
由于冰岛人口稀少(约36万),为避免近亲繁殖,当地人开发了专门的亲属关系检测App。本题要求我们实现这个App的核心算法,能够快速判断两个人是否在五代以内有共同祖先。
要实现高效的亲属关系检测,我们需要建立合适的数据结构来表示家族关系:
java复制static int[] father = new int[200005]; // 父节点索引
static int[] gender = new int[200005]; // 1:男, 0:女
static boolean[] isExist = new boolean[200005]; // 标记是否在正式名单中
static Map<String, Integer> nameToId = new HashMap<>(100005); // 姓名到ID的映射
这种设计有以下几个关键考虑:
检测两人关系的算法分为几个关键步骤:
输入预处理:
查询处理:
java复制if (任一姓名不存在) return "NA";
if (性别相同) return "Whatever";
if (异性) {
if (五代内无公共祖先) return "Yes";
else return "No";
}
五代检测算法:
处理维京姓氏时需要特别注意字符串截取的准确性:
java复制if (lName.endsWith("sson")) {
// 截取父亲名:去掉最后4个字符("sson")
int fid = getId(lName.substring(0, lName.length() - 4));
father[id] = fid;
gender[fid] = 1; // 父亲必须标记为男性
} else if (lName.endsWith("sdottir")) {
// 截取父亲名:去掉最后7个字符("sdottir")
int fid = getId(lName.substring(0, lName.length() - 7));
father[id] = fid;
gender[fid] = 1;
}
特别注意:即使父亲不在初始名单中,也需要为其创建记录并标记为男性,这是题目明确要求的。
传统方法需要为每次查询清空访问标记数组,这在Java中会导致性能问题。我们采用创新的标记系统:
java复制static int[] visitedTag = new int[200005];
static int[] depthA = new int[200005];
static boolean check(int a, int b, int tag) {
// 标记A的祖先链
int curr = a, d = 1;
while (curr != -1) {
visitedTag[curr] = tag;
depthA[curr] = d;
curr = father[curr];
d++;
}
// 检查B的祖先链
curr = b;
int d2 = 1;
while (curr != -1) {
if (visitedTag[curr] == tag) {
int d1 = depthA[curr];
if (d1 < 5 || d2 < 5) return false;
else return true;
}
curr = father[curr];
d2++;
}
return true;
}
这种方法利用查询编号(tag)区分不同查询的状态,避免了数组重置操作,将时间复杂度从O(N)降为O(1)。
由于输入规模可能达到10^5量级,使用标准Scanner会导致超时。我们实现了一个高效的自定义输入读取器:
java复制static class Reader {
private InputStream in;
private byte[] buf = new byte[1024 * 64];
private int ptr = 0, len = 0;
public int nextInt() throws Exception {
int c = read();
while (c >= 0 && c <= 32) c = read();
int res = 0;
while (c >= '0' && c <= '9') {
res = res * 10 + (c - '0');
c = read();
}
return res;
}
// 其他方法省略...
}
这个实现比标准Scanner快3-5倍,是解决Java超时问题的关键。
姓氏截取错误:
父亲性别标记遗漏:
五代判定逻辑错误:
建议设计以下测试场景:
基础维京姓氏解析
text复制1
erik eriksson
1
erik erik erik erik
混合性别判定
text复制2
alice smithf
bob adamsm
1
alice smith bob adam
五代边界检测
text复制6
a a
b asson
c bsson
d csson
e dsson
f esson
1
a a f e
避免对象创建:
减少GC压力:
IO优化:
预处理阶段:O(N)
查询阶段:O(M * K)
在PTA评测系统上:
这个家谱问题可以有多种变体:
双向关系检测:
更复杂的亲属关系:
动态家谱更新:
大规模分布式处理:
在实际工程中,这类亲属关系检测算法广泛应用于: