题意整理:

本题题意较为简单,实际上就是在给定字符串中找到是否存在一个子串,该子串能够表示为一个字符串的重复拼接。也既从字符串 a 中,找到 字符串 s, s能够表示为 的形式(其中竖线只是表示分割)。例如字符串 fabcabcd 中,存在子串 abcabc,能够表示为 ,此处 s = abcabc, w = abc。

方法一:枚举起点和长度

核心思想:

我们可以枚举一个字符串 w 的起点以及长度,判断在它的后面是否存在与它一样的字符串,如果存在,说明满足题目中要求的重复拼接形式。
具体实现时,对于字符串长度的枚举,可以从大到小枚举,这样找到答案后能够直接返回。其中枚举的起点为 ,即为字符串长度一半,因为长度大于字符串长度一半的子串,必然不存在同样的拷贝在其后面。
如果找到了满足题意的字符串 w ,则重复字符串为其重复出现的结果,即为其长度的两倍

核心代码:

class Solution {
public:
    //对具体的起点与长度,确认字符串是否为重复拼接形式
    bool check(string& a, int len, int begin) {
        for(int i = begin; i < len + begin; ++i) {
            if(a[i] != a[i + len]) {
                //这说明字符串肯定不存在重复拷贝在其后面,返回false
                return false;
            }
        }
        return true;//这说明在这个字符串后面存在它的拷贝
    }

    int solve(string a) {
        int n = a.length();
        for(int i = n / 2; i > 0; --i) {//枚举长度
            for(int j = 0; j <= n - i - i; ++j) {//枚举起点
                //起点要保证两个字符串拼接后长度不会大于原字符串
                if(check(a, i, j)) {
                    return 2 * i;//说明找到了重复字符串,长度即为枚举长度两边
                }
            }
        }
        return 0;
    }
};

复杂度分析:

时间复杂度:,枚举实际上采用了三层循环,第一层为对长度的枚举,第二层为对起点的枚举,第三层为判断是否存在重复拷贝时对字符串的遍历
空间复杂度:,只使用了常数个变量

方法二:优化枚举

核心思想:

方法一的时间复杂度为 ,这能够通过测试,但复杂度还是太高。可以考虑对三重循环进行优化降低复杂度。对于字符串长度的枚举是无法优化的,因为并没有什么规律能够对其进行二分或重复利用。第二重循环对字符串起点进行枚举,第三重循环对字符串进行检测,这是否能够优化呢?
考虑对字符串 的长度为 5 时的检查,下图将每次被第二重循环截取出用于check函数检查的子字符串显式的截取出来。
图片说明
可以发现,在第一次比较中,以及可以确定以 b,c,d 三个字符开头的长度为 5 的字符串不可能作为答案字符串,但后续依然以其作为开头进行了枚举,这种重复使得时间复杂度上升了。
那么如何避免这种重复比较呢?当我们在对一个对于长度 len 进行枚举字符串开头时,实际上是一直对 进行判断,如果枚举到一个字符 i 时判断不满足条件,那么在字符 i 以及它之前的字符为开头的字符串到 i 处都会不满足条件,所以可以从 i + 1 作为开头的字符串继续分析。这样,对于一个给定的长度,只需要对字符串进行一次遍历即可,使得时间复杂度降低了。
例如,依然对上述样例进行分析:当对子串 abcde 进行分析时,在字符 e 处发现不满足条件,那么同等长度的情况下,如果以字符 b 为开头,同样会遍历到字符 e 处,且在此处返回false,所以,可以从它的下一个字符开始分析。
具体实现中,使用一个变量记录当前满足条件的子串长度,如果长度到达当前枚举到的所需长度,返回长度的两倍,表示找到了答案

核心代码:

class Solution {
public:
    int solve(string a) {
        int n = a.length(), res = 0;
        for(int i = n / 2; i > 0; --i) {//枚举长度
            for(int j = 0; j < n - i; ++j) {//枚举起点
                if(a[j] == a[i + j]) {
                    ++res;
                } else {
                    res = 0;//不满足条件,重置长度,从下一个字符为起点开始分析
                }
                if(res == i) return i * 2;
            }
        }
        return 0;
    }
};

复杂度分析:

时间复杂度:,使用了两层循环,第一层为对长度的枚举,第二层为对起点的遍历。
空间复杂度:,只使用了常数个变量