相似和

给出n个字符串串,定义两个字符串的相似度为他们的最长公共前缀长度

案例
输入:3,["niuniu","niumei","niuneng"]
返回值:10
说明:
"niuniu"与"niumei"的相似度为3
"niuniu"与"niuneng"的相似度为4
"niumei"与"niuneng"的相似度为3
所以相似度的总和为10

方法一 暴力

遍历字符串数组对于第i个字符串需要再遍历i之后的所有字符串与第i字符串进行对比统计.

class Solution {
public:
    long long solve(int n, vector<string>& s) {
        int ans = 0;
        for(int i = 0; i < n; i ++){ // 循环第i个字符串
            for(int j = i + 1; j < n; j ++){ // 遍历第i个字符串之后的所有字符串
                for(int k = 0; k < min(s[i].size(), s[j].size()); k ++){ //判断字符串相同的位数
                    if(s[i][k] == s[j][k]) ans ++;
                    else break;
                }
            }
        }
        return ans;
    }
};

时间复杂度: 对于第i个字符串每次需要与i之后的所有字符进行对比
空间复杂度: 使用若干个变量

方法二 字典树

将所有的字符串存入字典树中并对每一个存入的字符位置进行计数,之后遍历字符串数组一一匹配答案,对于第i个字符串找寻时只需要将每次的结点值减取i这个位数(即字符串在数组的第几位)即可,详见如图
图片说明

class Solution {
public:
    int tree[1000010][30], cnt[1000010];
    int idx = 0;
    int push(string a, int ff, int id){ //字典树
        int p = 0, res = 0;
        for(int i = 0; i < a.size(); i ++){//将字符存入字符树中
            int ss = a[i] - 'a';
            if(!tree[p][ss]) tree[p][ss] = ++ idx;
            p = tree[p][ss];
            if(ff) cnt[p] ++; //对于每一位记录过的值进行次数统计
            res += max(cnt[p] - id, 0); //计算当前值对于总体的贡献
        }
        return res;
    }
    long long solve(int n, vector<string>& s) {
        // write code here
        int ans = 0;
        for(int i = 0; i < n; i ++) push(s[i], 1, 0);
        for(int i = 0; i < n; i ++) {
            ans += push(s[i], 0, i + 1);
        }
        cout <<endl;
        return ans;
    }
};

时间复杂度: 存储字符串到字典树与读取的复杂度
空间复杂度: 字典树的节点存储以及字符的访问次数