我有一个字符串S.如何找到字符串是否遵循S = nT。
示例:
如果是,则函数应返回true
1)S =“abab”
2)S =“abcdabcd”
3)S =“abcabcabc”
4)S =“zzxzzxzzx”
但是如果S =“abcb”返回false。
我想也许我们可以在S的子串上重复调用KMP然后决定。
例如:for“abab”:在“a”上拨打KMP。它返回2(两个实例)。现在2 * len(“a”)!= len(s)
在“ab”上打电话给KMP。它返回2.现在2 * len(“ab”)== len(s)所以返回true
你能建议更好的算法吗?
答案 0 :(得分:5)
我可以想到一个启发式方法,如果Len(原始字符串)/ Len(子字符串)是一个正整数,则只在子字符串上调用KMP。
此外,子字符串的最大长度必须小于N / 2.
使用这些启发式方法我写了下面的python代码,因为我的C现在生锈了
oldstr='ABCDABCD'
for i in xrange(0,len(oldstr)/2):
newslice=oldstr[0:i+1]
if newslice*(len(oldstr)/len(newslice)) == oldstr:
print 'pattern found', newslice
break
答案 1 :(得分:4)
您实际上只需要关心测试子串长度等于完整字符串长度除以素数。原因是:如果S包含n个副本,并且n不是素数,那么n = ab,因此S实际上也包含bT的副本(其中“bT”表示“T重复b次”)。这是anijhaw's answer的扩展名。
int primes[] = { 2, 3, 5, 7, 11, 13, 17 }; /* There are one or two more... ;) */
int nPrimes = sizeof primes / sizeof primes[0];
/* Passing in the string length instead of assuming ASCIIZ strings means we
* don't have to modify the string in-place or allocate memory for new copies
* to handle recursion. */
int is_iterative(char *s, int len) {
int i, j;
for (i = 0; i < nPrimes && primes[i] < len; ++i) {
if (len % primes[i] == 0) {
int sublen = len / primes[i];
/* Is it possible that s consists of repeats of length sublen? */
for (j = sublen; j < len; j += sublen) {
if (memcmp(s, s + j, sublen)) {
break;
}
}
if (j == len) {
/* All length-sublen substrings are equal. We could stop here
* (meaning e.g. "abababab" will report a correct, but
* non-minimal repeated substring of length 4), but let's
* recurse to see if an even shorter repeated substring
* can be found. */
return is_iterative(s, sublen);
}
}
}
return len; /* Could not be broken into shorter, repeated substrings */
}
请注意,在递归查找更短的重复子字符串时,我们不需要再次检查整个字符串,只需要检查第一个较大的重复字符串 - 因为我们已经确定剩余的大重复字符串是, >重复第一个。 :)
答案 2 :(得分:1)
在这种情况下,我没有看到KMP算法有帮助。这不是确定从哪里开始下一场比赛的问题。似乎减少搜索时间的一种方法是从最长的可能性(长度的一半)开始并向下工作。需要测试的唯一长度是均匀分成总长度的长度。这是Ruby中的一个例子。我应该补充一点,我意识到问题被标记为C
,但这只是一种简单的方式来显示我正在考虑的算法(并允许我测试它是否有效)。
class String
def IsPattern( )
len = self.length
testlen = len / 2
# the fastest is to start with two entries and work down
while ( testlen > 0 )
# if this is not an even divisor then it can't fit the pattern
if ( len % testlen == 0 )
# evenly divides, so it may match
if ( self == self[0..testlen-1] * ( len / testlen ))
return true
end
end
testlen = testlen - 1
end
# must not have matched
false
end
end
if __FILE__ == $0
ARGV.each do |str|
puts "%s, %s" % [str, str.IsPattern ? "true" : "false" ]
end
end
[C:\test]ruby patterntest.rb a aa abab abcdabcd abcabcabc zzxzzxzzx abcd
a, false
aa, true
abab, true
abcdabcd, true
abcabcabc, true
zzxzzxzzx, true
abcd, false
答案 3 :(得分:0)
我想您可以尝试以下算法:
允许L
成为生成原始单词的可能子串长度。对于从L
到1
的{{1}},检查第一个字符是否在strlen(s)/2
的所有L*i
位置从1到i
获得。如果确实如此,那么它可能是一个可能的解决方案,您应该使用strlen(s)/L
进行检查,如果没有尝试下一个memcmp
。当然,您可以跳过一些L
未划分的值L
。
答案 4 :(得分:0)
试试这个:
char s[] = "abcabcabcabc";
int nStringLength = strlen (s);
int nMaxCheckLength = nStringLength / 2;
int nThisOffset;
int nNumberOfSubStrings;
char cMustMatch;
char cCompare;
BOOL bThisSubStringLengthRepeats;
// Check all sub string lengths up to half the total length
for (int nSubStringLength = 1; nSubStringLength <= nMaxCheckLength; nSubStringLength++)
{
// How many substrings will there be?
nNumberOfSubStrings = nStringLength / nSubStringLength;
// Only check substrings that fit exactly
if (nSubStringLength * nNumberOfSubStrings == nStringLength)
{
// Assume it's going to be ok
bThisSubStringLengthRepeats = TRUE;
// check each character in substring
for (nThisOffset = 0; nThisOffset < nSubStringLength; nThisOffset++)
{
// What must it be?
cMustMatch = s [nThisOffset];
// check each substring's char in that position
for (int nSubString = 1; nSubString < nNumberOfSubStrings; nSubString++)
{
cCompare = s [(nSubString * nSubStringLength) + nThisOffset];
// Don't bother checking more if this doesn't match
if (cCompare != cMustMatch)
{
bThisSubStringLengthRepeats = FALSE;
break;
}
}
// Stop checking this substring
if (!bThisSubStringLengthRepeats)
{
break;
}
}
// We have found a match!
if (bThisSubStringLengthRepeats)
{
return TRUE;
}
}
}
// We went through the whole lot, but no matches found
return FALSE;
答案 5 :(得分:0)
这是Java代码,但你应该明白这个想法:
String str = "ababcababc";
int repPos = 0;
int repLen = 0;
for( int i = 0; i < str.length(); i++ ) {
if( repLen == 0 ) {
repLen = 1;
} else {
char c = str.charAt( i );
if( c == str.charAt( repPos ) ) {
repPos = ++repPos % repLen;
} else {
repLen = i+1;
}
}
}
如果没有重复,这将返回最短重复块的长度或字符串的长度。
答案 6 :(得分:0)
您可以构建字符串的后缀数组,对其进行排序 现在寻找一系列不断加倍的后缀,当你达到整个字符串(S)的大小时,系列中的第一个会给你T。
例如:
abcd <-- T
abcdabcd <-- S
bcd
bcdabcd
cd
cdabcd
d
dabcd
x
xzzx
xzzxzzx
zx
zxzzx
zxzzxzzx
zzx <-- T
zzxzzx
zzxzzxzzx <-- S
a
apa
apapa
apapapa
pa <-- T
papa
papapa <-- Another T, not detected by this algo
papapapa <-- S
答案 7 :(得分:0)
暴力破解方法是选择所有可能的子字符串,然后查看它们是否可以构成整个字符串。
使用以下观察结果,我们可以做得更好:将子字符串作为有效候选len(str) % len(substr) == 0
。这可以从问题陈述中得出。
这是完整的代码:
bool isRational(const string &str){
int len = str.length();
const auto &factors = getFactors(len); // this would include 1 but exclude len
// sort(factors.begin(), factors.end()); To get out of the loop faster. Why? See https://stackoverflow.com/a/4698155/1043773
for(auto iter = factors.rbegin(); iter != factors.rend(); ++iter){
auto factor = *iter;
bool result = true;
for(int i = 0; i < factor && result; ++i){
for(int j = i + factor; j < len; j += factor, ++cntr){
if (str[i] != str[j]) { result = false; break; }
}
}
if (result) { return true;}
}
return false;
}
请注意,使用KMP的时间复杂度变化更快。
上述算法为O(N * factorCount(N))
但是,这种算法的优点在于,它可以比KMP算法快得多。同样,因素的数量并没有增加太多。
这是[i, factorCount(i)] for i <= 10^6
的图
这是与KMP算法相比算法的性能。 红色图形为O(N * factorCount(N)),蓝色图形为O(N)KMP
从here提取KMP代码
答案 8 :(得分:0)
这是一个简单而有效的 C 语言解决方案:
memcmp
的单个调用来断言每个潜在模式长度的谓词。#include <string.h>
// check if string s is a repeated pattern.
// return the pattern length or 0.
size_t is_iterative(const char *s) {
size_t i, j, res = 0, len = strlen(s);
if (len > 1 && !memcmp(s, s + 1, len - 1))
return 1;
for (i = 2; i <= (j = len / i); i++) {
if (len % i == 0) {
if (!memcmp(s, s + i, len - i))
return i;
if (!memcmp(s, s + j, len - j)) {
res = len = j;
i--; // try the same divisor again
}
}
}
return res;
}
一种更快的方法是实施这些步骤:
a
个整数的数组 UCHAR_MAX+1
初始化为 0
。a
中。代码如下:
#include <limits.h>
#include <string.h>
size_t gcd_size(size_t a, size_t b) {
while (b != 0) {
size_t t = b;
b = a % b;
a = t;
}
return a;
}
// check if string s is a repeated pattern.
// return the pattern length or 0.
size_t is_iterative(const char *s) {
size_t a[UCHAR_MAX+1] = { 0 };
size_t i, j, plen, rep, len, res = 0;
for (i = 0; s[i]; i++) {
a[(unsigned char)s[i]]++;
}
rep = len = i;
if (rep <= 1)
return 0;
for (i = 0; i <= UCHAR_MAX; i++) {
if (a[i]) {
rep = gcd_size(a[i], rep);
if (rep == 1)
return 0;
}
}
plen = len / rep;
if (!memcmp(s, s + plen, len - plen))
return plen;
for (i = 2; i <= (j = rep / i); i++) {
if (rep % i == 0) {
plen = len / j;
if (!memcmp(s, s + plen, len - plen))
return plen;
plen = len / i;
if (!memcmp(s, s + plen, len - plen)) {
res = len = plen;
rep = j;
i--; // try the same divisor again
}
}
}
return res;
}
答案 9 :(得分:-1)
navigation stack