お世話になります。
主題の通りなのですが、配列から配列を検索する手法、または直接的なメソッドを探しています。
現在はとりあえず以下のようなコードで実現していますが、よりよい方法があればぜひお教えください。
また、C#に限らず他の言語でも、「この言語なら一行で書ける」等ございましたらお聞かせください。
lang
1// 拡張メソッド定義 2public static class IEnumerableExtensions 3{ 4 public static int IndexOf<T>(this IEnumerable<T> source, IList<T> list) 5 { 6 if (list == null || list.Count == 0) return -1; 7 8 int index = 0; 9 foreach (T item in source) 10 { 11 if (item != null && item.Equals(list[0])) 12 { 13 // 最初の1要素が一致したら切り出して比較 14 var part = source.Skip(index).Take(list.Count); 15 if (part.SequenceEqual(list)) return index; 16 } 17 ++index; 18 } 19 return -1; 20 } 21}
lang
1var sentence = "Lorem ipsum dolor sit amet, consectetur adipisicing elit".Select(Convert.ToByte); 2byte[] searchBytes = { 0x6f, 0x6c }; // 'o', 'l' 3Console.WriteLine(sentence.IndexOf(searchBytes)); // ==> 13
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
不具合あるかもしれませんが
C#
1public static class IEnumerableExtensions 2{ 3 public static IEnumerable<int> IndicesOf<T>(this IEnumerable<T> source, IList<T> target) 4 { 5 if (target == null || target.Count == 0) 6 return Enumerable.Empty<int>(); 7 return source 8 .Select((x, i) => new { index = i, value = source.Skip(i).Take(target.Count) }) 9 .Where(x => x.value.SequenceEqual(target)) 10 .Select(x => x.index); 11 } 12 13 public static int IndexOf<T>(this IEnumerable<T> source, IList<T> target) 14 { 15 return source.IndicesOf(target).DefaultIfEmpty(-1).First(); 16 } 17}
C#
1var source = new int[] { 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, }; 2var target = new int[] { 3, 4 }; 3 4foreach(var x in source.IndicesOf(target)) 5{ 6 Console.WriteLine(x); 7} 8//3 9//8 10 11Console.WriteLine(source.IndexOf(target)); 12//3
投稿2015/11/18 00:46
総合スコア13551
0
力押しでいいならRubyだと何も考えずに一行でかけます。
Ruby
1sentence = "Lorem ipsum dolor sit amet, consectetur adipisicing elit".each_byte.to_a 2searchBytes = [0x6f, 0x6c] # 'o', 'l' 3puts sentence.each_cons(searchBytes.size).find_index(searchBytes) # ==> 13
最悪計算量はO(mn)ですし、最悪m個もArray生成するし、速度は度外視です。
(yubaさんが言っているKMP法やBM法を応用したやりかたはちょっと後から試そうかな)
投稿2015/11/17 22:23
総合スコア21737
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
C#よりもC++の方が得意なので、まずはC++だとこんな感じというのをお見せします。
C++
1#include <iostream> 2#include <algorithm> 3#include <vector> 4 5int main() 6{ 7 std::string sentenceText = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; 8 std::vector<uint8_t> sentence(sentenceText.begin(), sentenceText.end()); 9 std::vector<uint8_t> searchBytes = {0x6f, 0x6c}; 10 11 // 1行で書けます 12 auto iter = std::search(sentence.begin(), sentence.end(), searchBytes.begin(), searchBytes.end()); 13 14 // 結果は13 15 std::cout << iter - sentence.begin() << std::endl; 16 return 0; 17}
C#でこんな感じに書けるのかどうか、私も興味あります。
投稿2015/11/17 15:44
総合スコア5942
0
ベストアンサー
文字列探索という課題になりますね。
質問文中でお示しの方法は、長さmの文字列中で長さnの文字列を探すのに最悪でほぼmn回の比較が発生し、効率的ではありません。
よく知られている(情報科学の講義で教わる)効率的な手法としてはKMP法、BM法というのがあります。その他に、BM法の改良型で実装しやすく効率も良い手法としてSunday法、またの名をクイックサーチと呼ぶものがあり、以前C#で書いたコードがあるので明日まだ質問が開いていたら載せます。
.NET Frameworkには文字列探索のメソッド用意されてないんですよね。
--- 追記
c#
1/// <summary> 2/// byte配列の中からbyteパターンと一致する並びを検索する。 3/// アルゴリズムはSunday法。 4/// </summary> 5/// <param name="pattern">探すパターン</param> 6/// <param name="text">探索範囲となる配列</param> 7/// <returns>発見した場合は開始位置、発見できなかった場合は-1</returns> 8public static int SearchBytes(this byte[] text, byte[] pattern) 9{ 10 int patternLen = pattern.Length, textLen = text.Length; 11 12 // 移動量テーブルの作成 13 int[] qs_table = new int[byte.MaxValue + 1]; 14 15 // デフォルト(パターン中に存在しないキャラクタが比較範囲の直後にあった)の場合、 16 // 次の比較範囲はそのキャラクタの次。(=比較範囲ずらし幅はパターン長+1) 17 for (int i = qs_table.Length; i-- > 0; ) qs_table[i] = patternLen + 1; 18 19 // パターンに存在するキャラクタが比較範囲の直後にあった場合、 20 // 次の比較範囲は、そのキャラクタとパターン中のキャラクタを一致させる位置に。 21 for (int n = 0; n < patternLen; ++n) qs_table[pattern[n]] = patternLen - n; 22 23 int pos; 24 25 // 移動量テーブルを用いて、文章の末尾に達しない範囲で比較を繰り返す 26 for (pos = 0; pos < textLen - patternLen; pos += qs_table[text[pos + patternLen]]) 27 { 28 // 一致するか比較。一致したら、そのときの比較位置を返す。 29 if (CompareBytes(text, pos, pattern, patternLen)) return pos; 30 } 31 32 // 文章の末尾がまだ未比較なら、そこも比較しておく 33 if (pos == textLen - patternLen) 34 { 35 // 一致するか比較。一致したら、そのときの比較位置を返す。 36 if (CompareBytes(text, pos, pattern, patternLen)) return pos; 37 } 38 39 // 一致する位置はなかった。 40 return -1; 41} 42 43/// <summary> 44/// 配列(pattern)が別の配列(text)に含まれているかを判定する。 45/// 46/// pos + patternLen が text.Length より大きかったり 47/// pos や patternLen が 0 未満だったり、 48/// needdleLen が pattern.Length より大きかったりすると 49/// ArrayOutOfBoundException が発生する。 50/// </summary> 51/// <param name="text">この配列の pos 番目からを pattern と比較する</param> 52/// <param name="pos">text のどこから比較するか</param> 53/// <param name="pattern">この配列全体が、text の pos 番目からと一致しているかを判定する</param> 54/// <param name="patternLen">patternのうち一致判定する長さ</param> 55/// <returns></returns> 56static bool CompareBytes(byte[] text, int pos, byte[] pattern, int patternLen) 57{ 58 for (int comparer = 0; comparer < patternLen; ++comparer) 59 { 60 if (text[comparer + pos] != pattern[comparer]) return false; 61 } 62 return true; 63} 64
投稿2015/11/17 16:05
編集2015/11/18 03:09総合スコア5570
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/11/17 16:34
2015/11/17 22:54
2015/11/18 03:14
2015/11/18 14:03
2015/11/18 14:43
2015/11/18 15:42
2015/11/18 16:03
2015/11/18 22:18
2015/11/19 06:41
2015/11/24 15:32
0
すみません、大勘違いしてしまいました...
csharp
1var elm = sentence.Select((v, i) => new {val = (byte)v, idx = i}).FirstOrDefault(f => searchBytes.Contains(f.val)); 2var index = (elm == null) ? -1 : elm.idx;
投稿2015/11/18 00:33
編集2015/11/18 00:36総合スコア728
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/11/18 01:22
2015/11/19 16:29
2015/11/24 15:39