16

私はACMティムスでこの問題を解決しようとしてきました

http://acm.timus.ru/problem.aspx?space=1&num=1932

私の最初のアプローチはO(n ^ 2)ですが、これは確かにすべてのテストに合格するのに十分な速さではありません。以下のO(n ^ 2)コードは、テスト10でTLを提供します。

import java.util.*;
import java.io.*;

public class testtest
{
    public static void main(String[] args) throws IOException
    {
        BufferedReader rr = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(rr.readLine());
        String[] p = new String[n];
        for (int i = 0; i < n; i ++)
        {
            p[i] = rr.readLine();
        }
        int[] diff = new int[]{0, 0, 0, 0};
        for (int i = 0; i < n - 1; i ++)
        {
            for (int j = i + 1; j < n; j ++)
            {
                int ans  = (p[i].charAt(0) == p[j].charAt(0) ? 0 : 1) +
                           (p[i].charAt(1) == p[j].charAt(1) ? 0 : 1) +
                           (p[i].charAt(2) == p[j].charAt(2) ? 0 : 1) +
                           (p[i].charAt(3) == p[j].charAt(3) ? 0 : 1);
                diff[ans - 1] ++;
            }
        }
        System.out.print(diff[0] + " " + diff[1] + " " + diff[2] + " " + diff[3]);
    }
}

このアプローチをより速くするためのアイデアはありますか?入力に使用できる文字のセットは限られていることに気付きました( '0' .. '9'、'a' ..'f')ので、配列を作成して(メモリ制限で十分です)、次の場合に高速チェックを実行できます。以前に文字が入力されています。

ありがとう...実際の実装は必要ありません。簡単なアイデア/考えがあれば素晴らしいと思います。 編集:素晴らしいアイデアをありがとう。ビットロジックを使用してO(n ^ 2)の改善を試みましたが、それでも制限時間を超えました。パスカルコードは以下のとおりです。

program Project2;

{$APPTYPE CONSOLE}

var
  i, j, n, k, bits: integer;
  arr: array[1..65536] of integer;
  diff: array[1..4] of integer;
  a, b, c, d: char;

function g(c: char): integer; inline;
begin
  if ((c >= '0') and (c <= '9')) then
  begin
    Result := Ord(c) - 48;
  end
  else
  begin
    Result := Ord(c) - 87;
  end;
end;

begin
  Readln(n);
  for i := 1 to n do
  begin
    Read(a); Read(b); Read(c); Readln(d);
    arr[i] := g(a) * 16 * 16 * 16 + g(b) * 16 * 16 + g(c) * 16 + g(d);
    for j := 1 to i - 1 do
    begin
      bits := arr[i] xor arr[j];
      k := ((bits or (bits shr 1) or (bits shr 2) or (bits shr 3)) and $1111) mod 15;
      Inc(diff[k]);
    end;
  end;
  Write(diff[1], ' ', diff[2], ' ', diff[3], ' ', diff[4]);
{$IFNDEF ONLINE_JUDGE}
  Readln;
{$ENDIF}
end.

だから私は他のより良い提案を試してみなければならないと思います。

編集: 私はダニエルのアルゴリズムを試しましたが、それは非常に有望です、おそらく以下のコードに間違いがあります、それはテスト10で間違った答えを取得し続けます...誰かが見てもらえますか?どうもありがとう...

import java.util.*;
import java.io.*;

public class testtest
{
    private static int g(char ch)
    {
        if ((ch >= '0') && (ch <= '9'))
        {
            return (int)ch - 48;
        }
        return (int)ch - 87;
    }

    public static void main(String[] args) throws IOException
    {
        BufferedReader rr = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(rr.readLine());
        int[] p = new int[n];
        int[] all = new int[65536];
        int[][] miss = new int[4][4096];
        int[] g12 = new int[256];
        int[] g13 = new int[256];
        int[] g14 = new int[256];
        int[] g23 = new int[256];
        int[] g24 = new int[256];
        int[] g34 = new int[256];
        int[][] gg = new int[4][16];
        int same3, same2, same1, same0, same4;
        for (int i = 0; i < n; i ++)
        {
            String s = rr.readLine();
            int x = g(s.charAt(0)) * 4096 + g(s.charAt(1)) * 256 + g(s.charAt(2)) * 16 + g(s.charAt(3));
            p[i] = x;
            all[x] ++;
            miss[0][x >> 4] ++;
            miss[1][(x & 0x000F) | ((x & 0xFF00) >> 4)] ++;
            miss[2][(x & 0x00FF) | ((x & 0xF000) >> 4)] ++;
            miss[3][x & 0x0FFF] ++;
            g12[x >> 8] ++;
            g13[((x & 0x00F0) >> 4) | ((x & 0xF000) >> 8)] ++;
            g14[(x & 0x000F) | ((x & 0xF000) >> 8)] ++;
            g23[(x & 0x0FF0) >> 4] ++;
            g24[(x & 0x000F) | ((x & 0x0F00) >> 4)] ++;
            g34[x & 0x00FF] ++;
            gg[0][x >> 12] ++;
            gg[1][(x & 0xF00) >> 8] ++;
            gg[2][(x & 0xF0) >> 4] ++;
            gg[3][x & 0xF] ++;
        }

        same4 = 0;
        for (int i = 0; i < 65536; i ++)
        {
            same4 += (all[i] - 1) * (all[i]) / 2;
        }

        same3 = 0;
        for (int i = 0; i < 4096; i ++)
        {
            same3 += (miss[0][i] - 1) * (miss[0][i]) / 2;
            same3 += (miss[1][i] - 1) * (miss[1][i]) / 2;
            same3 += (miss[2][i] - 1) * (miss[2][i]) / 2;
            same3 += (miss[3][i] - 1) * (miss[3][i]) / 2;
        }

        same2 = 0;
        for (int i = 0; i < 256; i ++)
        {
            same2 += (g12[i] - 1) * g12[i] / 2;
            same2 += (g13[i] - 1) * g13[i] / 2;
            same2 += (g14[i] - 1) * g14[i] / 2;
            same2 += (g23[i] - 1) * g23[i] / 2;
            same2 += (g24[i] - 1) * g24[i] / 2;
            same2 += (g34[i] - 1) * g34[i] / 2;
        }

        same1 = 0;
        for (int i = 0; i < 16; i ++)
        {
            same1 += (gg[0][i] - 1) * gg[0][i] / 2;
            same1 += (gg[1][i] - 1) * gg[1][i] / 2;
            same1 += (gg[2][i] - 1) * gg[2][i] / 2;
            same1 += (gg[3][i] - 1) * gg[3][i] / 2;
        }

        same3 -= 4 * same4;
        same2 -= 6 * same4 + 3 * same3;
        same1 -= 4 * same4 + 3 * same3 + 2 * same2;
        same0 = (int)((long)(n * (n - 1) / 2) - same4 - same3 - same2 - same1);
        System.out.print(same3 + " " + same2 + " " + same1 + " " + same0);
    }
}

編集 ついにACを取得しました...そのような素晴らしいアルゴリズムをダニエルに感謝します!

4

9 に答える 9

6

小さいnの場合、もちろん、各ペアをチェックするブルート フォースO(n²)アルゴリズムの方が高速であるため、アルゴリズムを切り替えるための適切なカットオフ ポイントを見つける必要があります。測定しなければ、封筒の裏側の考慮事項により、200 から 3000 の間の値が予想されます。

int16 進数として解析することにより、海賊 ID を に変換します。ID を格納します。

int[] pirates = new int[n];

最初に、同一の ID を持つ海賊のペアの数を数えます (このステップは省略できます。問題ステートメントには存在しないためです)。

int[] allFour = new int[65536];
for(int i = 0; i < n; ++i) {
    allFour[pirate[i]] += 1;
}
int fourIdentical = 0;
for(int i = 0; i < 65536; ++i) {
    fourIdentical += allFour[i]*(allFour[i] - 1) / 2;
}

次に、ID に同じニブルが 3 つある海賊のペアを数えます。

int oneTwoThree(int p) {
    return p >> 4;
}
int oneTwoFour(int p) {
    return (p & 0x000F) | ((p & 0xFF00) >> 4);
}
int oneThreeFour(int p) {
    return (p & 0x00FF) | ((p & 0xF000) >> 4);
}
int twoThreeFour(int p) {
    return p & 0x0FFF;
}

int[] noFour  = new int[4096];
int[] noThree = new int[4096];
int[] noTwo   = new int[4096];
int[] noOne   = new int[4096];

for(int i = 0; i < n; ++i) {
    noFour[oneTwoThree(pirate[i])] += 1;
    noThree[oneTwoFour(pirate[i])] += 1;
    noTwo[oneThreeFour(pirate[i])] += 1;
    noOne[twoThreeFour(pirate[i])] += 1;
}

int threeIdentical = 0;
for(int i = 0; i < 4096; ++i) {
    threeIdentical += noFour[i]*(noFour[i]-1) / 2;
}
for(int i = 0; i < 4096; ++i) {
    threeIdentical += noThree[i]*(noThree[i]-1) / 2;
}
for(int i = 0; i < 4096; ++i) {
    threeIdentical += noTwo[i]*(noTwo[i]-1) / 2;
}
for(int i = 0; i < 4096; ++i) {
    threeIdentical += noOne[i]*(noOne[i]-1) / 2;
}

しかし、4 つの同一のニブルを持つ海賊のすべてのペアは4 choose 3 = 4、3 つのニブルの可能な選択ごとに、ここで回数がカウントされているため、それを差し引く必要があります (まあ、問題のためではありませんが、原則として):

threeIdentical -= 4*fourIdentical;

次に、ID に 2 つの同一のニブルを持つ海賊のペアを数えます。

int oneTwo(int p) {
    return p >> 8;
}
int oneThree(int p) {
    return ((p & 0x00F0) >> 4) | ((p & 0xF000) >> 8);
}
int oneFour(int p) {
    return (p & 0x000F) | ((p & 0xF000) >> 8);
}
int twoThree(int p) {
    return (p & 0x0FF0) >> 4;
}
int twoFour(int p) {
    return (p & 0x000F) | ((p & 0x0F00) >> 4);
}
int threeFour(int p) {
    return p & 0x00FF;
}

256 の 6 つの配列を割り当て、場所およびのようintに、対応するニブルで海賊の数を数えます。ab

int twoIdentical = 0;
int[] firstTwo = new int[256];
for(int i = 0; i < n; ++i) {
    firstTwo[oneTwo(pirate[i])] += 1;
}
for(int i = 0; i < 256; ++i) {
    twoIdentical += firstTwo[i]*(firstTwo[i] - 1) / 2;
}
// analogous for the other five possible choices of two nibbles

しかし、4 つの同じニブルを持つ4 choose 2 = 6ペアはここで回数カウントされ、3 つの同じニブルを持つペアは3 choose 2 = 3回数カウントされるため、減算する必要があります。

twoIdentical -= 6*fourIdentical + 3*threeIdentical;

次に、1 つのニブルが同じペアの数です。必要な 4 つの関数と配列を推測できると思います。4 choose 1 = 4次に、同じニブルが 4 回あるペア、同じニブルが 33 choose 1 = 3回あるペア、および同じニブルが 2 回2 choose 1 = 2あるペアをカウントします。

oneIdentical -= 4*fourIdentical + 3*threeIdentical + 2*twoIdentical;

最後に、同一のニブルがないペアの数は

int noneIdentical = (int)((long)n*(n-1) / 2) - oneIdentical - twoIdentical - threeIdentical - fourIdentical;

long( のオーバーフローを避けるためのキャストn*(n-1))。

于 2012-11-18T13:49:27.487 に答える
2

これは、識別子のペアを比較するための高速な方法である可能性があります。両方の識別子が文字列ではなく16進数として読み込まれていることを前提としています。

int bits = p[i] ^ p[j];
int ans = ((bits | bits >> 1 | bits >> 2 | bits >> 3) & 0x1111) % 15;
diff[ans - 1]++;
于 2012-11-17T23:58:56.063 に答える
2

これは、IDリストを1回通過するだけで解決できるため、O(n)時間ですが、実装が時間内に実行されるように注意する必要があります。

等しい文字列のペアを見つける問題を考えてみましょう。これは、以前にマップで見つかった各文字列の数を追跡することで実行できます。

long pairs = 0;
Set<String, Long> map = new HashMap<String, Long>();
for(String id:ids) {
    if(map.containsKey(id)) {
        pairs += map.get(id);
        map.put(id, map.get(id) + 1);
    } else {
        map.put(id, 1);
    }
}

次に、1つの文字が異なるペアを見つけます。各文字を除いた文字列を保存できます。したがって、「abcd」の場合は、「。bcd」、「a.cd」、「ab.d」、および「abc」を格納します。マップで。IDごとに、欠落している文字位置ごとに、各文字列のカウントを合計できます。これも等しい文字列をカウントするので、等しい文字列のカウントを減算します。

包含/除外の原則を使用して、より多くの欠落している文字に一般化します。たとえば、から2つの異なる文字を含む文字列の数sは次のとおりです。

sum of all character positions `x` and `y`:
    the number of strings equal to `s` excluding characters `x` and `y`
    - the number of strings equal to `s` excluding character `x`
    - the number of strings equal to `s` excluding character `y`
    + the number of strings equal to `s`

効率上の理由から、文字列の操作は行わないため、実際の実装では文字列を整数に変換する必要があります。また、マップの代わりに長い配列を使用できます(インデックスは文字列を表し、値は一致する文字列の数です)。

于 2012-11-18T15:14:28.683 に答える
2

これはどう:

16各次元が要素幅で、すべての要素が に初期化された整数の 4 次元配列を作成します0。したがって、16*16*16*16*4 = 262144それは約262KB. ここで、読み取り時に各識別子をこの構造体にインデックス化 (挿入) します。インデックスを作成するときはcounter、各次元でインクリメントします...

この構造にインデックスを付けているので、必要な答えを段階的に計算できます。

たとえば、識別子deadのインデックスを作成しているとします。最初の次元で、dに対応する位置を選択します。この位置のカウンターが であるとしましょうn1。これは、これまでのところ、位置 1にdn1を持つ識別子があったことを意味します。次に、 eを取得して、2 番目の次元の正しい位置に挿入します。 ...そして、この位置のカウンターが であると言うと、挿入されている現在の識別子と比較して3つの位置が異なる識別子があることは確かです...n2n1 - n2

編集:私は自分自身を疑い始めています。2 つの識別子の最初の文字が異なり、2 番目の文字が等しい場合はどうなるでしょうか? このアプローチでは、私が思うにそれを拾うことができなくなります。もっと考える必要があります...

編集:「スマートソリューション」で一時停止した後、O(n^2)以下のように基本アルゴリズムを改善しようとしました。

各識別子には 4 桁の 16 進数があるため、1 つの整数にパックできます (ただし、2 バイトの単語で十分です)。したがって、そのような整数は次のようになります。

[ ][ ][ ][ ] * [ ][ ][ ][ ] * [ ][ ][ ][ ] * [ ][ ][ ][ ]

それぞれ[ ]がビットを表し、各[ ][ ][ ][ ]グループ (半バイト) は識別子の 16 進数に対応します。ここで、これらの識別子のうち 2 つをすばやく比較する方法が必要です。2 つの識別子aとが与えられるとb、まず を計算します。これにより、2 つのビット パターンのXORc = a ^ bが得られます (差、一種)。次に、このビット パターンを次の定数パターンと比較します。c

u = [1][1][1][1] * [0][0][0][0] * [0][0][0][0] * [0][0][0][0]

v = [0][0][0][0] * [1][1][1][1] * [0][0][0][0] * [0][0][0][0]

w = [0][0][0][0] * [0][0][0][0] * [1][1][1][1] * [0][0][0][0]

x = [0][0][0][0] * [0][0][0][0] * [0][0][0][0] * [1][1][1][1]

比較は以下のようなものです。

diffs = 0

if (c & u)
  diffs++
if (c & v)
  diffs++
if (c & w)
  diffs++
if (c & x)
  diffs++

この操作の最後に、とが異なるdiffs文字数が含まれます。識別子を整数にパックせずに同じアプローチを実装できることに注意してください(つまり、各16進数を独自の整数/文字型に保持します)が、このパックによって節約されるメモリは何かを意味するかもしれないと思いました.ab

O(n^2)IMHO、これは基本的なアルゴリズムに対するわずかな改善にすぎません。受け入れられた答えがこの種のトリックであり、より良いアルゴリズム/データ構造に基づく答えではない場合、私はかなりがっかりするでしょう. 誰かがそのような答えについてアドバイスをくれるのを待ち望んでいます... :-)

于 2012-11-18T01:17:29.630 に答える
1

この回答は、@ fgb / @Daniel によって提供された回答の詳細です。

H1H2H3H4各識別子を(16進数4桁)のように表しましょう

  1. それぞれが length の4 つの配列があります16*16*16 (4096)。これらの 4 つの配列は、3 つの位置で等しい識別子を追跡するために使用されます。

  2. 読み取られた識別子ごとに、値 (整数) H2H3H4H1H3H4H1H2H4を計算します (H1H2H3これらの値の範囲は 0 ~ 4095 であることに注意してください。そのため、4096 の配列サイズを選択しました)。次に、これらの値のそれぞれを使用して、手順 (1) で説明した 4 つの配列にインデックスを付け、対応する位置を 1 つ増やします。

  3. それぞれが length の別の6 つの配列があります16*16 (256)。これらの 5 つの配列は、2 つの位置で等しい識別子を追跡するために使用されます。

  4. 読み取られた識別子ごとに、値 (整数) H3H4H2H4H1H4H1H3H1H2を計算しますH2H3。次に、これらの値のそれぞれを使用して、手順 (3) で説明した 6 つの配列にインデックスを付け、対応する位置を 1 つ増やします。各配列位置は、2 つの位置で等しい識別子の数を効果的にカウントします (ただし、このカウントには、3 つの位置で等しい識別子の数も含まれることに注意してください)。

  5. さらに4 つの配列があり、それぞれが width16です。これらの 4 つの配列は、1 つの位置で等しい識別子を追跡するために使用されます。

  6. 読み取った識別子ごとに、値 (整数) H4H3H2、を計算しますH1。次に、これらの値のそれぞれを使用して、手順 (5) で説明した 4 つの配列にインデックスを付け、対応する位置を 1 つ増やします。各配列位置は、1 つの位置で等しい識別子の数を効果的にカウントします (繰り返しますが、このカウントには、3、2 の位置で等しい識別子の数も含まれます)。

これらはすべて、入力に対する 1 回のパスで実行できることに注意してください。ただし、これは最終的な答えを計算するのに十分ではありません。ステップ 1 ~ 6 に沿って、最終的な答えを段階的に計算する必要があります。これを行う方法は次のとおりです。

diff1 = 0
diff2 = 0
diff3 = 0
diff4 = 0
  • 手順 (2) で、配列の位置をインクリメントすると、結果の値が の場合、 になりu (u > 1)ますdiff1 = diff1 + (u - 1)。これは、(u - 1)すでに見つかった識別子を使用して、(u - 1)新しいペアを作成できることを意味します (それぞれを現在の識別子とペアにすることによって)。

  • v (v - u >= 1)手順 (4) で、配列の位置をインクリメントすると、結果の値がdiff2 = diff2 + (v - u). これは、前のステップですでに多くの識別子が考慮されていることがわかっているためu、それらを再カウントするべきではないことを意味します。したがって、(v - u)この識別子と等しい/異なる他の識別子を2つの位置でのみ与えます。これらの識別子を使用して、(v - u)新しいペアを作成できます (それぞれを現在の識別子とペアにすることによって)。

  • w (w - u >= 1)手順 (6) で、配列の位置をインクリメントすると、結果の値がdiff3 = diff3 + (w - u). 説明は上記のものと非常に似ています。

これらの手順はすべて、入力全体に対する 1 回のパスで実行できることに注意してください。

さて、次は計算方法diff4です。diff4 = diff4 + 1 これはステップ (2) で実行できます。新しい値が 1の場合は配列の位置をインクリメントし、新しい値が 2 の場合はdiff4 = diff4 - 1. これにより、これまで一致する識別子が見つからなかった新しい識別子が追跡されます。

更新:上記のアプローチはうまく機能しますが、私が提案した計算方法は機能しdiff4ません。基本的には一意の識別子ではなく、4 つの位置すべてで互いにdiff4異なる識別子を組み合わせて、いくつのペアを形成できるかということです。

diff4C での次の実装では、別のアプローチ ( for ) が採用されています。

#include <stdio.h>
#include <stdlib.h>

// forward declarations
__inline int update_same3 (int x1, int x2, int x3, int* map, int* diff1, int* overlaps);
__inline int update_same2 (int x1, int x2, int u, int* map, int* diff2, int* overlaps);
__inline void update_same1 (int x1, int v, int* map, int* diff3, int* overlaps);

int main () {
  // number of input identifiers
  int n = 0;

  // each identifier represented as four hexa-decimal digits
  int h1, h2, h3, h4; 

  // final answer
  int diff1 = 0;
  int diff2 = 0;
  int diff3 = 0;
  int diff4 = 0;

  int i, u1, u2, u3, u4, v1, v2, v3, v4, v5, v6, overlaps;

  // maps for keeping track of the identifiers
  int* maps [14];
  maps[0] = (int*) calloc (4096, sizeof(int)),  // h1h2h3
  maps[1] = (int*) calloc (4096, sizeof(int)),  // h2h2h4
  maps[2] = (int*) calloc (4096, sizeof(int)),  // h1h3h4
  maps[3] = (int*) calloc (4096, sizeof(int)),  // h2h3h4
  maps[4] = (int*) calloc (256, sizeof(int)),  // h1h2
  maps[5] = (int*) calloc (256, sizeof(int)),  // h1h3
  maps[6] = (int*) calloc (256, sizeof(int)),  // h1h4
  maps[7] = (int*) calloc (256, sizeof(int)),  // h2h3
  maps[8] = (int*) calloc (256, sizeof(int)),  // h2h4
  maps[9] = (int*) calloc (256, sizeof(int)),  // h3h4
  maps[10] = (int*) calloc (16, sizeof(int)),  // h1
  maps[11] = (int*) calloc (16, sizeof(int)),  // h2
  maps[12] = (int*) calloc (16, sizeof(int)),  // h3
  maps[13] = (int*) calloc (16, sizeof(int)),  // h4

  // main loop
  fscanf (stdin, "%d\n", &n);
  for (i = 0; i < n; i++) {
    // scan identifier 
    fscanf (stdin, "%1x%1x%1x%1x", &h1, &h2, &h3, &h4);

    // keeps track of the number of identifiers that the current
    // identifier overlaps with, required for calculating diff4
    overlaps = 0;

    u1 = update_same3 (h1, h2, h3, maps[0], &diff1, &overlaps); //h1h2h3
    u2 = update_same3 (h1, h2, h4, maps[1], &diff1, &overlaps); //h1h2h4
    u3 = update_same3 (h1, h3, h4, maps[2], &diff1, &overlaps); //h1h3h4
    u4 = update_same3 (h2, h3, h4, maps[3], &diff1, &overlaps); //h2h3h4

    v1 = update_same2 (h1, h2, u1 + u2, maps[4], &diff2, &overlaps); //h1h2
    v2 = update_same2 (h1, h3, u1 + u3, maps[5], &diff2, &overlaps); //h1h3
    v3 = update_same2 (h1, h4, u2 + u3, maps[6], &diff2, &overlaps); //h1h4
    v4 = update_same2 (h2, h3, u1 + u4, maps[7], &diff2, &overlaps); //h2h3
    v5 = update_same2 (h2, h4, u2 + u4, maps[8], &diff2, &overlaps); //h2h4
    v6 = update_same2 (h3, h4, u3 + u4, maps[9], &diff2, &overlaps); //h3h4

    update_same1 (h1, (v1 + v2 + v3) - (u1 + u2 + u3), maps[10], &diff3, &overlaps); //h1
    update_same1 (h2, (v1 + v4 + v5) - (u1 + u2 + u4), maps[11], &diff3, &overlaps); //h2
    update_same1 (h3, (v2 + v4 + v6) - (u1 + u3 + u4), maps[12], &diff3, &overlaps); //h3
    update_same1 (h4, (v3 + v5 + v6) - (u2 + u3 + u4), maps[13], &diff3, &overlaps); //h4

    // if the current identifier overlapped with n existing identifiers
    // then it did not overlap with (i - n) identifiers
    if (i - overlaps > 0)
      diff4 += (i - overlaps); 
  }

  // print results
  printf ("%d %d %d %d\n", diff1, diff2, diff3, diff4);
}

// identify overlaps at 3 positions
__inline int update_same3 (int x1, int x2, int x3, int* map, int* diff1, int* overlaps) {
  int idx = (x1 << 8 | x2 << 4 | x3);
  int u = (map[idx])++;
  if (u > 0) {
    (*diff1) += u;
    (*overlaps) += u;
  }
  return u;
}

// identify overlaps at 2 positions
__inline int update_same2 (int x1, int x2, int u, int* map, int* diff2, int* overlaps) {
  int idx = (x1 << 4 | x2);
  int v = (map[idx])++;
  if (v - u > 0) {
    (*diff2) += (v - u);
    (*overlaps) += (v - u);
  }
  return v;
}

// identify overlaps at 1 position
__inline void update_same1 (int x1, int v, int* map, int* diff3, int* overlaps) {
  int w = (map[x1])++;
  if (w - v > 0) {
    (*diff3) += (w - v);
    (*overlaps) += (w - v);
  }
}

私自身のいくつかのサンプル入力でこれをテストしましたが、うまくいくようです。OPがこれをテストして、機能するかどうかを確認できれば素晴らしいでしょう。もう少し作業が必要かもしれません。

更新: OK、動作します! コンパイラを介してコードを取得するには、コードを少し再フォーマットする必要がありました。変更を反映するために、ここにリストされているコードも更新しました。

于 2012-11-19T03:13:48.367 に答える
1

ペアを数える代わりに、マッチを数えてからペアを計算できますか? すべて d で始まる IE 5 コードは、実際には 5 になります。= 10 ペア。

これにより、反復ではなく挿入時に位置一致をカウントできる構造を使用できます。

編集:

当時、ルート ノードと 4 つのネストされたレベルを持つツリー構造を持つことを考えていました。各レベルは、コード内の 1 つの位置を表します。IE ルート -> d -> e -> a -> f. 各コードを挿入するときに、その文字がそのレベルに存在しない場合は挿入し、集計する場合は挿入します。各コード挿入の最後に、そのコードが何回一致したかがわかります。これは、 の適切なバケットに追加されdiffます。次に、n! を取って、単に一致するのではなくペアに変換します。各バケットの。

于 2012-11-17T23:46:04.807 に答える
1

n!/(n-2)!2! =O(n 2 ) ペア (n - 入力数)があり、何をするにしても、これが要件であるため、すべてをチェックする必要があります。

入力全体をDAWGに挿入し、後でトラバースすることで、時間を最適化できます。DAWG の任意の 2 つのパスの類似性は、両方のパスに存在するエッジから構成されるセットとして定義されます。このセットを構築し、パスの長さからそのサイズを差し引いて、距離の差を見つけます。

于 2012-11-17T23:51:06.993 に答える
0

これらの行に沿って何かを試していただけますか:

各エントリを処理するときに、特定の文字が 1 桁目にある名前のリストに追加します。リスト内のすべてのエントリは、すでに「4 つの一致候補」のリストに含まれています。

次に、特定の 2 番目の文字を持つ名前のリストに追加します。このリストに既にあるエントリは、「4 つの一致候補」にあり、「4 つの一致する候補」に残ります。「4 つの一致する候補」の他のすべてのエントリは、このリストのすべてのエントリとともに「3 つの一致する候補」のリストに移動されます。 .

次に、特定の 3 番目の文字を持つ名前のリストに追加します。このリストに既にあるエントリは、「3 つの一致候補」にあり、「3 つの一致候補」に残ります。「3 つの一致候補」の他のすべてのエントリは、このリストにすでにあるすべてのエントリとともに「2 つの一致候補」のリストに移動されます。リスト。このリストの「4 つの一致候補」にあるエントリは「4 つの一致候補」に残り、「4 つの一致候補」の他のすべてのエントリは「3 つの一致候補」のリストに移動されます。

次に、特定の 4 番目の文字を持つ名前のリストに追加します。このリストに既にあるエントリは、1 つの一致候補のカウントに追加されます。「2 つの一致する候補」リスト内のエントリは、2 つの一致する候補のカウントに追加されます。「3 つの一致する候補」リスト内のエントリは、 3 つの一致候補のカウント、および「4 つの一致候補」リスト内のすべてのエントリが 4 つの一致候補のカウントに追加されます。

これは、リストを一度だけ処理し、他のすべてのエントリをチェックするのではなく、同じ文字が同じ位置にあるサブセットをチェックするだけでよいことを (うまくいけば) 意味するはずです。

それとも寝なきゃいけないのかな…

編集:

それで、昨夜の目的を誤解しました。上記のプロセスは相違点ではなく類似点をカウントしますが、同様のアプローチを使用して相違点をカウントできます。基本的には、1 異語、2 異語、3 異語、4 異語の 4 つのリストを保持します。これまでに見たすべての単語を 4 つの違いの単語のリストに追加します。次に、すべての既存の名前を含むリストから始めて、現在の単語の最初の文字が最初の場所にある単語のリストを検索します。このリスト内の 2 差リストにあるすべての単語を 1 差リストに、3 差リストを 2 差リストに、4 差リストを 3 差リストに移動します。

すべてのリストを処理したら、各リストの count をグローバル カウントに追加できます。

これでうまくいくと思いますが、後で試します。..

于 2012-11-18T00:32:40.253 に答える