思路:

题目的主要信息:

  • 字符串的相似度定义为两个字符串的相同字符前缀长度
  • 对于n个字符串,求任意两个字符串的相似度,并求总和
  • 只出现小写字母a-z

方法一:暴力法
具体做法:

class Solution {
public:
    int similarity(string& s1, string& s2){ //计算两个字符串相似度的函数
        int len1 = s1.length();
        int len2 = s2.length();
        int n = min(len1, len2); //遍历较小的
        int res = 0;
        for(int i = 0; i < n; i++){
            if(s1[i] == s2[i])  //前缀相似字符数
                res++;
            else
                break;
        }
        return res;
    }
    long long solve(int n, vector<string>& s) {
        long long res = 0;
        for(int i = 0; i < n; i++){ //找到任意两个字符串
            for(int j = i + 1; j < n; j++){
                res += similarity(s[i], s[j]);
            }
        }
        return res;
    }
};

复杂度分析:

  • 时间复杂度:,外层循环为,内存循环为个计算相似度的函数相当于遍历所有字符串长度和,即
  • 空间复杂度:,常数空间

方法二:字典树
具体做法:
因为要对每一个字符串和剩余的全部字符串比较,我们可以创建字典树,沿着树枝比较,每次重复加路径上的计数即可。如图所示简约字典树,只有abc三个字母:
图片说明

class Solution {
public:
    struct Tree{
        Tree* letter[26]; //26个小写字母
        int count;  //前缀计数
        Tree(){    //初始化函数
            for(int i = 0; i < 26; i++)
                letter[i] = NULL;
            count = 0;
        }
    };
    long long solve(int n, vector<string>& s) {
        long long res = 0;
        Tree* root = new Tree();
        for(int i = 0; i < n; i++){
            Tree* p = root;
            for(int j = 0; j < s[i].length(); j++){
                if(p->letter[s[i][j] - 'a'] == NULL) //如果对应字符为空,创建新树
                    p->letter[s[i][j] - 'a'] = new Tree();
                p = p->letter[s[i][j] - 'a']; //进入子树
                res += (long long)(p->count); //累加
                p->count++; //前缀计数
            }
        }
        return res;
    }
};

复杂度分析:

  • 时间复杂度:,相当于遍历所有的字符串
  • 空间复杂度:,树空间最深处为最长字符串的长度