通过迭代子字符串“n”次来查找给定字符串是否可以从子字符串中表示
给定一个字符串'str',检查它是否可以通过获取它的子字符串并将子字符串的多个副本附加在一起来构造。
例子:
Input: str = "abcabcabc"
Output: true
The given string is 3 times repetition of "abc"
Input: str = "abadabad"
Output: true
The given string is 2 times repetition of "abad"
Input: str = "aabaabaabaab"
Output: true
The given string is 4 times repetition of "aab"
Input: str = "abcdabc"
Output: false
资料来源:谷歌面试问题
我们强烈建议您单击此处并进行练习,然后再继续使用解决方案。
这个问题可以有很多解决方案。具有挑战性的部分是在 O(n) 时间内解决问题。下面是一个 O(n) 算法。
设给定字符串为'str',给定字符串的长度为'n'。
1) 找出 'str' 的最长正确前缀的长度,它也是一个后缀。让最长的正确前缀后缀的长度为'len'。这可以使用 KMP字符串匹配算法的预处理步骤在 O(n) 时间内计算。
2)如果'n-len'的值除以n(或'n %(n-len)'为0),则返回true,否则返回false。
在 'true' 的情况下,子字符串 'str[0..n-len-1]' 是重复 n/(n-len) 次的子字符串。
让我们举几个例子。
输入:str = “ABCDABCD”,n = 8('str' 中的字符数)
len 的值为 4(“ABCD”是最长的子串,既是前缀又是后缀)
因为 (n-len) 除以 n,所以答案是正确的。
输入:str = “ABCDABC”,n = 7('str' 中的字符数)
len 的值为 3(“ABC”是最长的子串,既是前缀又是后缀)
因为 (n-len) 不整除 n,所以答案是错误的。
输入:str = “ABCABCABCABCABC”,n = 15('str'中的字符数)
len 的值为 12(“ABCABCABCABC”是最长的子串,既是前缀又是后缀)
因为 (n-len) 除以 n,所以答案是正确的。
这是如何运作的?
最长正确前缀后缀(或 len)的长度始终在 0 到 n-1 之间。如果 len 为 n-1,则字符串中的所有字符都相同。例如,“AAAA”的 len 为 3。如果 len 为 n-2 且 n 为偶数,则字符串中的两个字符重复 n/2 次。例如“ABABABAB”,lps的长度为6。原因是如果前n-2个字符与最后n-2个字符相同,则从第一对开始,每对字符都与下一对相同。下图演示了长度为 4 的子字符串的相同情况。
以下是上述算法的实现:
C++
// A C++ program to check if a string is 'n' times
// repetition of one of its substrings
#include
#include
using namespace std;
// A utility function to fill lps[] or compute prefix function
// used in KMP string matching algorithm. Refer
// https://www.geeksforgeeks.org/archives/11902 for details
void computeLPSArray(char str[], int M, int lps[])
{
int len = 0; //length of the previous longest prefix suffix
int i;
lps[0] = 0; //lps[0] is always 0
i = 1;
// the loop calculates lps[i] for i = 1 to M-1
while (i < M)
{
if (str[i] == str[len])
{
len++;
lps[i] = len;
i++;
}
else // (pat[i] != pat[len])
{
if (len != 0)
{
// This is tricky. Consider the example AAACAAAA
// and i = 7.
len = lps[len-1];
// Also, note that we do not increment i here
}
else // if (len == 0)
{
lps[i] = 0;
i++;
}
}
}
}
// Returns true if str is repetition of one of its substrings
// else return false.
bool isRepeat(char str[])
{
// Find length of string and create an array to
// store lps values used in KMP
int n = strlen(str);
int lps[n];
// Preprocess the pattern (calculate lps[] array)
computeLPSArray(str, n, lps);
// Find length of longest suffix which is also
// prefix of str.
int len = lps[n-1];
// If there exist a suffix which is also prefix AND
// Length of the remaining substring divides total
// length, then str[0..n-len-1] is the substring that
// repeats n/(n-len) times (Readers can print substring
// and value of n/(n-len) for more clarity.
return (len > 0 && n%(n-len) == 0)? true: false;
}
// Driver program to test above function
int main()
{
char txt[][100] = {"ABCABC", "ABABAB", "ABCDABCD", "GEEKSFORGEEKS",
"GEEKGEEK", "AAAACAAAAC", "ABCDABC"};
int n = sizeof(txt)/sizeof(txt[0]);
for (int i=0; i
Java
// Java program to check if a string is 'n'
// times repetition of one of its substrings
import java.io.*;
class GFG {
// A utility function to fill lps[] or compute
// prefix function used in KMP string matching
// algorithm. Refer
// https://www.geeksforgeeks.org/archives/11902
// for details
static void computeLPSArray(String str, int M,
int lps[])
{
// length of the previous
// longest prefix suffix
int len = 0;
int i;
lps[0] = 0; // lps[0] is always 0
i = 1;
// the loop calculates lps[i]
// for i = 1 to M-1
while (i < M)
{
if (str.charAt(i) == str.charAt(len))
{
len++;
lps[i] = len;
i++;
}
else // (pat[i] != pat[len])
{
if (len != 0)
{
// This is tricky. Consider the
// example AAACAAAA and i = 7.
len = lps[len-1];
// Also, note that we do
// not increment i here
}
else // if (len == 0)
{
lps[i] = 0;
i++;
}
}
}
}
// Returns true if str is repetition of
// one of its substrings else return false.
static boolean isRepeat(String str)
{
// Find length of string and create
// an array to store lps values used in KMP
int n = str.length();
int lps[] = new int[n];
// Preprocess the pattern (calculate lps[] array)
computeLPSArray(str, n, lps);
// Find length of longest suffix
// which is also prefix of str.
int len = lps[n-1];
// If there exist a suffix which is also
// prefix AND Length of the remaining substring
// divides total length, then str[0..n-len-1]
// is the substring that repeats n/(n-len)
// times (Readers can print substring and
// value of n/(n-len) for more clarity.
return (len > 0 && n%(n-len) == 0)? true: false;
}
// Driver program to test above function
public static void main(String[] args)
{
String txt[] = {"ABCABC", "ABABAB", "ABCDABCD",
"GEEKSFORGEEKS", "GEEKGEEK",
"AAAACAAAAC", "ABCDABC"};
int n = txt.length;
for (int i = 0; i < n; i++) {
if(isRepeat(txt[i]) == true)
System.out.println("True");
else
System.out.println("False");
}
}
}
// This code is contributed by Prerna Saini
Python3
# A Python program to check if a string is 'n' times
# repetition of one of its substrings
# A utility function to fill lps[] or compute prefix function
# used in KMP string matching algorithm. Refer
# https://www.geeksforgeeks.org/archives/11902 for details
def computeLPSArray(string, M, lps):
length = 0 # length of the previous longest prefix suffix
i = 1
lps[0] = 0 # lps[0] is always 0
# the loop calculates lps[i] for i = 1 to M-1
while i < M:
if string[i] == string[length]:
length += 1
lps[i] = length
i += 1
else:
if length != 0:
# This is tricky. Consider the example AAACAAAA
# and i = 7.
length = lps[length-1]
# Also, note that we do not increment i here
else:
lps[i] = 0
i += 1
# Returns true if string is repetition of one of its substrings
# else return false.
def isRepeat(string):
# Find length of string and create an array to
# store lps values used in KMP
n = len(string)
lps = [0] * n
# Preprocess the pattern (calculate lps[] array)
computeLPSArray(string, n, lps)
# Find length of longest suffix which is also
# prefix of str.
length = lps[n-1]
# If there exist a suffix which is also prefix AND
# Length of the remaining substring divides total
# length, then str[0..n-len-1] is the substring that
# repeats n/(n-len) times (Readers can print substring
# and value of n/(n-len) for more clarity.
if length > 0 and n%(n-length) == 0:
return True
else:
False
# Driver program
txt = ["ABCABC", "ABABAB", "ABCDABCD", "GEEKSFORGEEKS",
"GEEKGEEK", "AAAACAAAAC", "ABCDABC"]
n = len(txt)
for i in range(n):
if isRepeat(txt[i]):
print (True)
else:
print (False)
# This code is contributed by BHAVYA JAIN
C#
// C# program to check if a string is 'n'
// times repetition of one of its substrings
using System;
class GFG {
// A utility function to fill lps[] or
// compute prefix function used in KMP
// string matching algorithm. Refer
// https://www.geeksforgeeks.org/archives/11902
// for details
static void computeLPSArray(String str, int M,
int []lps)
{
// length of the previous
// longest prefix suffix
int len = 0;
int i;
lps[0] = 0; // lps[0] is always 0
i = 1;
// the loop calculates lps[i]
// for i = 1 to M-1
while (i < M)
{
if (str[i] == str[len])
{
len++;
lps[i] = len;
i++;
}
else // (pat[i] != pat[len])
{
if (len != 0)
{
// This is tricky. Consider the
// example AAACAAAA and i = 7.
len = lps[len-1];
// Also, note that we do
// not increment i here
}
else // if (len == 0)
{
lps[i] = 0;
i++;
}
}
}
}
// Returns true if str is repetition of
// one of its substrings else return false.
static bool isRepeat(String str)
{
// Find length of string and create
// an array to store lps values used
// in KMP
int n = str.Length;
int[] lps = new int[n];
// Preprocess the pattern (calculate
// lps[] array)
computeLPSArray(str, n, lps);
// Find length of longest suffix
// which is also prefix of str.
int len = lps[n-1];
// If there exist a suffix which is also
// prefix AND Length of the remaining
// substring divides total length, then
// str[0..n-len-1] is the substring that
// repeats n/(n-len) times (Readers can
// print substring and value of n/(n-len)
// for more clarity.
return (len > 0 && n % (n - len) == 0)
? true : false;
}
// Driver program to test above function
public static void Main()
{
String[] txt = {"ABCABC", "ABABAB",
"ABCDABCD", "GEEKSFORGEEKS",
"GEEKGEEK", "AAAACAAAAC",
"ABCDABC"};
int n = txt.Length;
for (int i = 0; i < n; i++)
{
if(isRepeat(txt[i]) == true)
Console.WriteLine("True");
else
Console.WriteLine("False");
}
}
}
// This code is contributed by Sam007.
Javascript
Java
/*package whatever //do not write package name here */
import java.io.*;
class GFG {
public static int findPeriod(String A)
{
int length = A.length();
// Initially consider there is no period for given
// String
int period = -1;
/*set two pointers one(i) at
index 0 and other(j) at index 1. increment j till
first character is obtained in the string*/
int i = 0;
for (int j = 1; j < length; j++) {
int currChar = A.charAt(j);
int comparator = A.charAt(i);
/*If current character matches with first
*character
*update period as the difference of j and i*/
if (currChar == comparator) {
period = (j - i);
i++;
}
/* if any mismatch occurs in between set i to
* zero also check if current character again
* matches
* with starting character. If matches, update
* period*/
else {
if (currChar == A.charAt(0)) {
i = 1;
period = j;
}
else {
i = 0;
period = -1;
}
}
}
/*check if the period is exactly dividing
* string if not reset the period to -1
* this eliminates partial substrings like
* e.g string -"GEEKSFORGEEKS" */
period = (length % period != 0) ? -1 : period;
return period;
}
public static void main(String[] args)
{
String[] testStrings
= { "ABCABC", "ABABAB", "ABCDABCD",
"GEEKSFORGEEKS", "GEEKGEEK", "AAAACAAAAC",
"ABCDABC" };
int n = testStrings.length;
for (int i = 0; i < n; i++) {
if (findPeriod(testStrings[i]) == -1)
System.out.println("false");
else
System.out.println("True");
}
}
}
输出:
True
True
True
False
True
True
False
时间复杂度:上述解决方案的时间复杂度为 O(n),因为它使用的是线性时间算法的 KMP 预处理算法。
另一种方法:
上述问题可以在不使用 KMP 算法和额外空间的情况下解决。
这种方法首先使用两个指针来检查字符串的最小周期。字符串的周期是前缀子字符串的长度,可以重复 x(x=length/period) 次来构造给定的字符串。
例如:输入字符串“ abcabcabcabc ”的句点为 3。这意味着我们可以通过重复前 3 个字符4(长度/3=4)次来构造给定的字符串。
方法:
1) 最初将第一个指针 - i 设置在给定字符串的第 0 个索引处,将第二个指针 - j 设置在第一个索引处。
2)检查两个索引处的字符。如果两者都匹配,则将句点作为 (ji) 并增加 i 和 j。
3) 如果不匹配,再次检查当前字符是否与第 0 个索引处的第一个字符匹配。如果匹配,将周期更新为 j(j-0=j) 并将 i 设置为下一个字符。
4) 如果两个字符都不匹配,则将 i 设置为 0,将更新周期设置为 -1。
5)最后检查计算的周期是否正好除以字符串的长度。如果不是,则将周期更新为-1。此检查消除了以小于句点的后缀结尾的字符串。例如,字符串“GEEKSFORGEEKS”的句点计算为 8,但后缀字符串(GEEKS) 只有 5 个字符且不完整。
Java
/*package whatever //do not write package name here */
import java.io.*;
class GFG {
public static int findPeriod(String A)
{
int length = A.length();
// Initially consider there is no period for given
// String
int period = -1;
/*set two pointers one(i) at
index 0 and other(j) at index 1. increment j till
first character is obtained in the string*/
int i = 0;
for (int j = 1; j < length; j++) {
int currChar = A.charAt(j);
int comparator = A.charAt(i);
/*If current character matches with first
*character
*update period as the difference of j and i*/
if (currChar == comparator) {
period = (j - i);
i++;
}
/* if any mismatch occurs in between set i to
* zero also check if current character again
* matches
* with starting character. If matches, update
* period*/
else {
if (currChar == A.charAt(0)) {
i = 1;
period = j;
}
else {
i = 0;
period = -1;
}
}
}
/*check if the period is exactly dividing
* string if not reset the period to -1
* this eliminates partial substrings like
* e.g string -"GEEKSFORGEEKS" */
period = (length % period != 0) ? -1 : period;
return period;
}
public static void main(String[] args)
{
String[] testStrings
= { "ABCABC", "ABABAB", "ABCDABCD",
"GEEKSFORGEEKS", "GEEKGEEK", "AAAACAAAAC",
"ABCDABC" };
int n = testStrings.length;
for (int i = 0; i < n; i++) {
if (findPeriod(testStrings[i]) == -1)
System.out.println("false");
else
System.out.println("True");
}
}
}
True
True
True
false
True
True
false
此方法由Sai Krishna Sribhshyam 贡献,如果有任何不正确的地方或您想分享任何进一步的信息,请发表评论。