
2026-05-18:统计凯撒加密对数目。用go语言,给定一个长度都为 m、只含小写字母的字符串数组 words(共 n 个)。如果可以通过对两个字符串 s、t 反复执行同一种“整体循环右移字母”的操作,使它们最终完全一致,那么称 s 与 t 相似。
具体操作是:你可以任选一个当前的字符串,把其中每个字母都替换成字母表中紧接着的下一个字母(例如 z 变成 a)。你可以对 s 或 t 进行任意次(可能为 0 次)这种操作。
现在要统计满足 i < j 且 words[i] 与 words[j] 相似的下标对 (i, j) 的数量,并返回这个数量。
1 <= n == words.length <= 100000。
1 <= m == words[i].length <= 100000。
1 <= n * m <= 100000。
words[i] 仅由小写英文字母组成。
输入: words = ["ab","aa","za","aa"]。
输出: 2。
解释:
words[0] = "ab" 和 words[2] = "za" 是相似的。words[1] = "aa" 和 words[3] = "aa" 是相似的。
题目来自力扣3805。
["ab","aa","za","aa"]"ab":出现 1 次"aa":出现 2 次"za":出现 1 次这是判断相似的关键:把所有相似的字符串,转换成同一个唯一的特征字符串。 转换规则: 对一个字符串,计算每个字符和第一个字符的相对偏移(循环26个字母),最终得到一个只由相对差值组成的新字符串,这就是它的标准化特征。
aa-a=0,b-a=1 → 特征字符串:[0,1]aa-a=0,a-a=0 → 特征字符串:[0,0]zz-z=0,a-z=1(循环后)→ 特征字符串:[0,1]✅ 结论:
ab 和 za 特征相同 → 相似aa 特征相同 → 相似使用哈希表记录每个特征字符串已经出现的总次数,结合组合数学计算对数:
组合公式:从 c 个相同元素中选 2 个的数量 = c*(c-1)/2
[0,1](对应原始字符串ab,数量1)[0,1] → 1[0,0](对应原始字符串aa,数量2)[0,0] → 2[0,1](对应原始字符串za,数量1)[0,1] → 1+1=2最终统计的有效对数为 2,和题目要求的输出完全一致。
O(n × m)
n×m ≤ 10^5,这个复杂度完全满足要求。O(n × m)
.
package main
import (
"fmt"
)
func countPairs(words []string)int64 {
cntWords := map[string]int{}
for _, s := range words {
cntWords[s]++
}
ans := 0
cnt := map[string]int{}
for s, c := range cntWords {
t := []byte(s)
base := t[0]
for i := range t {
t[i] = (t[i] - base + 26) % 26// 保证结果在 [0, 25] 中
}
s = string(t)
ans += cnt[s]*c + c*(c-1)/2// c 个 s 中选 2 个有 C(c, 2) 种方案
cnt[s] += c
}
returnint64(ans)
}
func main() {
words := []string{"ab", "aa", "za", "aa"}
result := countPairs(words)
fmt.Println(result)
}

.
# -*-coding:utf-8-*-
def count_pairs(words):
# Count occurrences of each word
cnt_words = {}
for s in words:
cnt_words[s] = cnt_words.get(s, 0) + 1
ans = 0
cnt = {}
for s, c in cnt_words.items():
# Convert string to list of bytes (characters)
t = list(s)
base = ord(t[0]) # Get ASCII value of first character
# Transform each character
for i in range(len(t)):
# (t[i] - base + 26) % 26
t[i] = chr((ord(t[i]) - base + 26) % 26 + ord('a'))
transformed_s = ''.join(t)
# ans += cnt[s]*c + c*(c-1)//2
ans += cnt.get(transformed_s, 0) * c + c * (c - 1) // 2
# cnt[s] += c
cnt[transformed_s] = cnt.get(transformed_s, 0) + c
return ans
def main():
words = ["ab", "aa", "za", "aa"]
result = count_pairs(words)
print(result)
if __name__ == "__main__":
main()
.
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <map>
using namespace std;
long long countPairs(vector<string>& words) {
unordered_map<string, int> cntWords;
for (conststring& s : words) {
cntWords[s]++;
}
long long ans = 0;
unordered_map<string, int> cnt;
for (const auto& entry : cntWords) {
string s = entry.first;
int c = entry.second;
string t = s;
char base = t[0];
for (int i = 0; i < t.length(); i++) {
t[i] = (t[i] - base + 26) % 26; // 保证结果在 [0, 25] 中
}
ans += (long long)cnt[t] * c + (long long)c * (c - 1) / 2;
cnt[t] += c;
}
return ans;
}
int main() {
vector<string> words = {"ab", "aa", "za", "aa"};
long long result = countPairs(words);
cout << result << endl;
return0;
}
