2

文字列のコレクションがあり、すべてが異なる最初のインデックスを知る必要があります。これを行うには 2 つの方法が考えられます。

最初の方法:

var minLength = [go through all strings finding min length];
var set = new set()
for(i=0;i<minlength;i++)
{
  for(str in strings)
  {
    var substring = str.substring(0,i);
    if(set.contains(substring))
      break; // not all different yet, increment i
    set.add(substring)
  }
  set.clear(); // prepare for next length of substring
}

これは、必要ではないように思われるセットのデータ構造を使用しているため、私にはひどいものです。

第二の方法:

var minLength = [go through all strings finding min length];
strings.sort();
for(i=0;i<minlength;i++)
{
  boolean done = true;
  char last = null;
  for(str in strings)
  {
    char c = str[i];
    if(c == last)
    {
      // not all different yet, increment i
      done = false;
      break;
    }
    last = c;
  }
  if(done)
    return i;
}

しかし、ソート アルゴリズムはその性質上、探している情報にアクセスできるため、最初にソートを実行しなければならないことに悩まされます。

きっと、私が上にリストしたものよりも効率的な方法があるに違いありません. 最終的には、任意のタイプの配列に抽象化したいと考えていますが、それは些細なことであり、文字列の問題と考える方が簡単です。

何か助けはありますか?

**更新: どうやら自分のことをうまく説明できていませんでした。文字列が ["apple", "banana", "cucumber", "banking"] の場合、インデックス 0 で一致する 2 つの文字列 ("banana" と "banking") があったため、関数は 3 を返します。 1、および 2 であるため、3 はそれらがすべて一意である最初のインデックスです。

Daniel が以下で述べたように、私のニーズをより適切に述べるには、「すべての文字列で substring(0,i) を呼び出すとすべての一意の値になるインデックス i を見つけたい」**

4

6 に答える 6

3

これはテストされていませんが、これが私の試みです。(必要以上に複雑にしているかもしれませんが、別の見方だと思います。)

基本的な考え方は、最初の要素で一致するアイテムのグループをコンパイルし、各グループの最大一意インデックスを見つけて、連続する各インデックスで要素をチェックすることです。

int FirstUniqueIndex<T>(IEnumerable<IEnumerable<T>> myArrayCollection)
{
    //just an overload so you don't have to specify index 0 all the time
    return FirstUniqueIndex(myArrayCollection, 0);
}

int FirstUniqueIndex<T>(IEnumerable<IEnumerable<T>> myArrayCollection, int StartIndex)
{
    /* Group the current collection by the element at StartIndex, and
     * return a collection of these groups. Additionally, we're only interested
     * in the groups with more than one element, so only get those.*/

    var groupsWithMatches = from var item in myArrayCollection //for each item in the collection (called "item")
                            where item.Length > StartIndex //that are long enough
                            group by item[StartIndex] into g //group them by the element at StartIndex, and call the group "g"
                            where g.Skip(1).Any() //only want groups with more than one element
                            select g; //add the group to the collection

    /* Now "groupsWithMatches" is an enumeration of groups of inner matches of
     * your original arrays. Let's process them... */

    if(groupsWithMatches.Any()) 
        //some matches were found - check the next index for each group
        //(get the maximum unique index of all the matched groups)
        return groupsWithMatches.Max(group => FirstUniqueIndex(group, StartIndex + 1));
    else
        //no matches found, all unique at this index
        return StartIndex;
}

上記の非 LINQ バージョンの場合 (List コレクションを使用するように変更しますが、どのコレクションでも構いません)。ラムダも削除します。これもテストしていないので、鋭利な器具を私の方向に向けないようにしてください。

int FirstUniqueIndex<T>(List<List<T>> myArrayCollection, int StartIndex)
{
    /* Group the current collection by the element at StartIndex, and
     * return a collection of these groups. Additionally, we're only interested
     * in the groups with more than one element, so only get those.*/

    Dictionary<T, List<List<T>>> groupsWithMatches = new Dictionary<T, List<List<T>>>();

    //group all the items by the element at StartIndex
    foreach(var item in myArrayCollection)
    {
        if(item.Count > StartIndex)
        {
            List<List<T>> group;
            if(!groups.TryGetValue(item[StartIndex], out group))
            {
                //new group, so make it first
                group = new List<List<T>>();
                groups.Add(item[StartIndex], group);
            }

            group.Add(Item);
        }
    }

    /* Now "groups" is an enumeration of groups of inner matches of
     * your original arrays. Let's get the groups with more than one item. */

    List<List<List<T>>> groupsWithMatches = new List<List<List<T>>>(groups.Count);

    foreach(List<List<T> group in groupsWithMatches)
    {
        if(group.Count > 1)
            groupsWithMatches.Add(group);
    }

    if(groupsWithMatches.Count > 0)
    {
        //some matches were found - check the next index for each group
        //(get the maximum unique index of all the matched groups)

        int max = -1;
        foreach(List<List<T>> group in groupsWithMatches)
        {
            int index = FirstUniqueIndex(group, StartIndex + 1);
            max = index > max ? index : max;
        }
        return max;
    }
    else
    {
        //no matches found, all unique at this index
        return StartIndex;
    }
}
于 2009-05-20T16:57:48.923 に答える
2

パトリシアトライを見たことがありますか?( Google コードで利用可能な Java 実装)

代替テキスト

トライを構築し、データ構造をトラバースして、すべての内部ノードの最大文字列位置を見つけます (上記の関数の黒い点)。

これは O(n) 操作のようです。あなたのセットの実装が O(n) かどうかはわかりません.O(n 2 )のような「におい」がありますが、わかりません.

于 2009-05-20T21:31:01.023 に答える
1

あなたが提案したようにセットを使用してください。それはまさに正しいことです。

于 2009-05-20T16:40:03.530 に答える
1

並べ替えを行わずに、最悪の場合でも各文字列の各文字を 1 回だけ確認するだけで、これを実行できるはずです。

インデックスをコンソールに表示する ruby​​ スクリプトを次に示します。

mystrings = ["apple", "banana", "cucumber", "banking"]
minlength = getMinLengthString(mystrings) #not defined here

char_set = {}

(0..minlength).each do |char_index|
  char_set[mystrings[0][char_index].chr] = 1
  (1..mystrings.length).each do |string_index|
    comparing_char = mystrings[string_index][char_index].chr
    break if char_set[comparing_char]
    if string_index == (mystrings.length - 1) then
      puts string_index
      exit
    else
      char_set[comparing_char] = 1
    end     
  end
  char_set.clear
end
puts minlength

結果は 3 です。

C# での同じ一般的なスニペットを次に示します。

string[] mystrings = { "apple", "banana", "cucumber", "banking" };

//defined elsewhere...
int minlength = GetMinStringLengthFromStringArray(mystrings);

Dictionary<char, int> charSet = new Dictionary<char, int>();

for (int char_index = 0; char_index < minlength; char_index++)
{
    charSet.Add(mystrings[0][char_index], 1);

    for (int string_index = 1; string_index < mystrings.Length; string_index++)
    {
        char comparing_char = mystrings[string_index][char_index];

        if (charSet.ContainsKey(comparing_char))
        {
             break;
        }
        else
        {
             if (string_index == mystrings.Length - 1)
             {
                  Console.Out.WriteLine("Index is: " + string_index.ToString());
                  return;
             }
             else
             {
                  charSet.Add(comparing_char, 1);
             }
        }
    }

    charSet.Clear();
}
Console.Out.WriteLine("Index is: " + minlength.ToString());
于 2009-05-20T17:14:18.513 に答える
0
int i = 0;
while(true)
{
    Set set = new Set();
    for(int j = 0; j < strings.length; j++)
    {
         if(i >= strings[j].length) return i;
         String chr = strings[j].charAt(i);
         if(set.hasElement(chr))
             break;
         else
             set.addElement(chr);
    }
    if(set.size() == strings.length)
        return i;
    i++;
}

まず前提条件を確認する必要があります。

編集:今セットを使用しています。言語を変更しました。

于 2009-05-20T16:40:31.763 に答える
0

Pythonでの私のソリューションは次のとおりです。

words = ["apple", "banana", "cucumber", "banking"]

for i in range(len(min(words))):
    d = defaultdict(int)
    for word in words:
        d[word[i]] += 1
    if max(d.values()) == 1:
        return i

最短の単語の終わりに到達するまでに最小インデックスが見つからない場合を処理するために何も書いていませんが、あなたはその考えを理解できると確信しています.

于 2009-05-20T21:12:11.903 に答える