私の問題は、指定された配列内の文字の繰り返しシーケンスを見つけることです。単に、文字が表示されるパターンを識別するためです。
.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
1: | J | A | M | E | S | O | N | J | A | M | E | S | O | N |
'---'---'---'---'---'---'---'---'---'---'---'---'---'---'
.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
2: | R | O | N | R | O | N | R | O | N | R | O | N | R | O | N |
'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'
.---.---.---.---.---.---.---.---.---.---.---.---.
3: | S | H | A | M | I | L | S | H | A | M | I | L |
'---'---'---'---'---'---'---'---'---'---'---'---'
.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
4: | C | A | R | P | E | N | T | E | R | C | A | R | P | E | N | T | E | R |
'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'
以前のデータを考えると、結果は次のようになります。
"JAMESON"
"RON"
"SHAMIL"
"CARPENTER"
あなたの例では、私の最初のアプローチは
C
になります)CARPENTER
)。もちろん、これは可能な配列の非常に限定されたサブセットに対してのみ機能します。同じWordが最初から始まり、途中に浮遊文字がなく、Word内で最初の文字が繰り返されない場合に、繰り返し繰り返されます。しかし、あなたのすべての例はこのカテゴリに分類されます-そして私はおそらくうまくいく可能性がある最も簡単な解決策を好みます:-)
繰り返されるWordに最初の文字が複数回含まれている場合(CACTUS
など)、アルゴリズムを拡張して、最初の文字だけでなく、その文字の後続の出現も探すことができます(そのため、繰り返されるWord全体が見つかります。その部分文字列だけではありません)。
この拡張アルゴリズムは、2番目の例とは異なる結果、つまりRONRON
ではなくRON
を返すことに注意してください。
舌の頬O(NlogN)ソリューション
文字列に対してFFTを実行します(文字を数値として扱います)。結果のグラフのすべてのピークは、サブストリングの周期性に対応しています。
Pythonでは、次のように正規表現を利用できます。
def recurrence(text):
import re
for i in range(1, len(text)/2 + 1):
m = re.match(r'^(.{%d})\1+$'%i, text)
if m: return m.group(1)
recurrence('abcabc') # Returns 'abc'
これがJavaまたはCにどのように変換されるかはわかりません。これがPythonが好きな理由の1つだと思います。:-)
最初に、以下のように、コンテナ文字列で繰り返し部分文字列sub
を見つけるメソッドを記述します。
boolean findSubRepeating(String sub, String container);
コンテナ内の部分文字列を増やしてこのメソッドを呼び出し続けます。最初に1文字の部分文字列を試し、次に2文字などをcontainer.length/2
まで試します。
私の頭に浮かぶ最初のアイデアは、length(S)= Nを分割する長さのすべての反復シーケンスを試すことです。そのような長さは最大N/2なので、これはO(N ^ 2)アルゴリズムになります。
しかし、それは改善できると確信しています...
疑似コード
len = str.length
for (i in 1..len) {
if (len%i==0) {
if (str==str.substr(0,i).repeat(len/i)) {
return str.substr(0,i)
}
}
}
注:簡潔にするために、私は文字列の「繰り返し」メソッドを発明しています。これは、実際にはJavaの文字列の一部ではありません。 "abc" .repeat(2)= "abcabc"
C++の使用:
//Splits the string into the fragments of given size
//Returns the set of of splitted strings avaialble
set<string> split(string s, int frag)
{
set<string> uni;
int len = s.length();
for(int i = 0; i < len; i+= frag)
{
uni.insert(s.substr(i, frag));
}
return uni;
}
int main()
{
string out;
string s = "carpentercarpenter";
int len = s.length();
//Optimistic approach..hope there are only 2 repeated strings
//If that fails, then try to break the strings with lesser number of
//characters
for(int i = len/2; i>1;--i)
{
set<string> uni = split(s,i);
if(uni.size() == 1)
{
out = *uni.begin();
break;
}
}
cout<<out;
return 0;
}
すべてのキャラクターを配列e.xに配置します。 a []
i=0; j=0;
for( 0 < i < count )
{
if (a[i] == a[i+j+1])
{++i;}
else
{++j;i=0;}
}
次に、(i/j)の比率=配列の繰り返し回数。 i
とj
の制限に注意する必要がありますが、これは簡単な解決策です。
そしてここに具体的な作業例があります:
/* find greatest repeated substring */
char *fgrs(const char *s,size_t *l)
{
char *r=0,*a=s;
*l=0;
while( *a )
{
char *e=strrchr(a+1,*a);
if( !e )
break;
do {
size_t t=1;
for(;&a[t]!=e && a[t]==e[t];++t);
if( t>*l )
*l=t,r=a;
while( --e!=a && *e!=*a );
} while( e!=a && *e==*a );
++a;
}
return r;
}
size_t t;
const char *p;
p=fgrs("BARBARABARBARABARBARA",&t);
while( t-- ) putchar(*p++);
p=fgrs("0123456789",&t);
while( t-- ) putchar(*p++);
p=fgrs("1111",&t);
while( t-- ) putchar(*p++);
p=fgrs("11111",&t);
while( t-- ) putchar(*p++);
これは問題に対するより一般的な解決策です。これは、シーケンス内のサブシーケンスの繰り返しを検出します。サブシーケンスは、最初から開始する必要も、互いにすぐ後に続く必要もありません。
問題のデータを含むシーケンスb [0..n]と、検出する最小のサブシーケンス長であるしきい値tが与えられると、
l_max = 0, i_max = 0, j_max = 0;
for (i=0; i<n-(t*2);i++) {
for (j=i+t;j<n-t; j++) {
l=0;
while (i+l<j && j+l<n && b[i+l] == b[j+l])
l++;
if (l>t) {
print "Sequence of length " + l + " found at " + i + " and " + j);
if (l>l_max) {
l_max = l;
i_max = i;
j_max = j;
}
}
}
}
if (l_max>t) {
print "longest common subsequence found at " + i_max + " and " + j_max + " (" + l_max + " long)";
}
基本的に:
これはキューを使用して思いついた解決策であり、コードフォースの同様の問題のすべてのテストケースに合格しました。問題Noは745Aです。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
string s, s1, s2; cin >> s; queue<char> qu; qu.Push(s[0]); bool flag = true; int ind = -1;
s1 = s.substr(0, s.size() / 2);
s2 = s.substr(s.size() / 2);
if(s1 == s2)
{
for(int i=0; i<s1.size(); i++)
{
s += s1[i];
}
}
//cout << s1 << " " << s2 << " " << s << "\n";
for(int i=1; i<s.size(); i++)
{
if(qu.front() == s[i]) {qu.pop();}
qu.Push(s[i]);
}
int cycle = qu.size();
/*queue<char> qu2 = qu; string str = "";
while(!qu2.empty())
{
cout << qu2.front() << " ";
str += qu2.front();
qu2.pop();
}*/
while(!qu.empty())
{
if(s[++ind] != qu.front()) {flag = false; break;}
qu.pop();
}
flag == true ? cout << cycle : cout << s.size();
return 0;
}
これを自分で理解し、多くのコメントを付けて(C#で記述された)このためのコードをいくつか記述しました。これが誰かを助けることを願っています:
// Check whether the string contains a repeating sequence.
public static bool ContainsRepeatingSequence(string str)
{
if (string.IsNullOrEmpty(str)) return false;
for (int i=0; i<str.Length; i++)
{
// Every iteration, cut down the string from i to the end.
string toCheck = str.Substring(i);
// Set N equal to half the length of the substring. At most, we have to compare half the string to half the string. If the string length is odd, the last character will not be checked against, but it will be checked in the next iteration.
int N = toCheck.Length / 2;
// Check strings of all lengths from 1 to N against the subsequent string of length 1 to N.
for (int j=1; j<=N; j++)
{
// Check from beginning to j-1, compare against j to j+j.
if (toCheck.Substring(0, j) == toCheck.Substring(j, j)) return true;
}
}
return false;
}
動作する理由が不明な場合は、遠慮なく質問してください。
「効率的に」を定義する方法がわからない。簡単/高速な実装では、Javaでこれを行うことができます。
private static String findSequence(String text) {
Pattern pattern = Pattern.compile("(.+?)\\1+");
Matcher matcher = pattern.matcher(text);
return matcher.matches() ? matcher.group(1) : null;
}
最短の文字列(.+?
)少なくとも1回繰り返す必要があります(\1+
)入力テキスト全体と一致します。
配列を文字列オブジェクトに変換し、正規表現を使用します