16

我一直在尝试在 ACM Timus 上解决这个问题

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.

所以我想,我必须尝试其他更好的建议..

编辑: 我已经尝试过 Daniel 的算法,它非常有前途,也许下面的代码有错误,在测试 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);
    }
}

编辑 终于得到了交流...感谢丹尼尔如此出色的算法!

4

9 回答 9

6

对于 small n,检查每一对的蛮力O(n²)算法当然更快,因此人们希望找到一个好的切换算法的截止点。在没有测量的情况下,由于信封背面的考虑,我预计值在 200 到 3000 之间。

通过将海盗 IDint解析为十六进制数字,将其转换为 。将 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 中具有三个相同半字节的海盗对,

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 choose 3 = 4多次,对于三个半字节的每个可能选择,所以我们需要减去它(好吧,不是为了问题,而是原则上):

threeIdentical -= 4*fourIdentical;

然后,计算 ID 中有两个相同半字节的海盗对:

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;
}

分配 6 个 256 ints 的数组并计算海盗的数量,其中相应的半字节位于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 choose 2 = 6了,而具有三个相同的半字节的对已经被计数了3 choose 2 = 3,所以我们需要减去

twoIdentical -= 6*fourIdentical + 3*threeIdentical;

接下来,具有一个相同半字节的对数。我相信您可以猜到需要哪些四个函数和数组。然后我们将计算具有四个相同半字节的对,计算4 choose 1 = 4具有三个相同半字节3 choose 1 = 3的对,以及具有两个相同半字节的对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

这可能是比较标识符对的快速方法。它假定两个标识符都作为十六进制数字而不是字符串读入。

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

这个怎么样:

创建一个 4 维整数数组,每个维度都是16元素宽,所有元素都初始化为0。所以,这将是16*16*16*16*4 = 262144字节,大约是262KB. 现在,在读取每个标识符时将它们索引(插入)到该结构中。counter索引时,在每个维度上递增...

当您对该结构进行索引时,您可以逐步计算所需的答案。

例如,假设我正在索引标识符dead。在第一个维度上,选择d对应的位置,假设这个位置的计数器是n1,这意味着到目前为止,在位置 1已经有dn1的标识符。现在取e并将其插入到第二个维度上的正确位置...并说这个位置的计数器是,那么我肯定知道与插入的当前标识符相比,存在 3 个位置不同的标识符...n2n1 - n2

编辑:我开始怀疑自己。如果两个标识符在第一个字符中不同但在第二个字符中相等怎么办?这种方法将无法接受我的想法。需要多思考...

编辑:暂停“智能解决方案”后,我尝试改进基本O(n^2)算法,如下所示。

每个标识符都有 4 个十六进制数字,因此可以打包成一个整数(不过一个 2 字节的字就足够了)。所以,这样的整数看起来像:

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

其中每个[ ]代表一个位,每个[ ][ ][ ][ ]组(半字节)对应于标识符中的一个十六进制数字。现在我们需要一种方法来快速比较这两个标识符。给定两个标识符ab,首先我们计算c = a ^ b,这将给我们两个位模式的XOR (差异,排序)。现在我们将此位模式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将包含与ab不同的字符数。请注意,可以在不将标识符打包成整数的情况下实现相同的方法(即,将每个十六进制数字保留在它自己的整数/字符类型上),但我认为这种打包节省的内存可能意味着什么。

恕我直言,这只是对基本O(n^2)算法的微小改进。如果接受的答案是这种诡计而不是基于更好的算法/数据结构的答案,我会非常失望。急切地等待有人就这样的答案提供一些建议...... :-)

于 2012-11-18T01:17:29.630 回答
2

这可以通过单次遍历 id 列表来解决,因此需要 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);
    }
}

现在找到一个字符不同的对。您可以存储不包括每个字符的字符串。所以对于“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`

出于效率原因,您不想进行字符串操作,因此实际实现应将字符串转换为整数。此外,您可以使用长数组代替 Map(其中索引表示字符串,值是匹配字符串的计数)。

于 2012-11-18T15:14:28.683 回答
1

除了计算对,你可以只计算匹配然后计算对吗?以 d 开头的 IE 5 代码实际上是 5!= 10 对。

这将允许您使用可以在插入时而不是迭代地计算位置匹配的结构。

编辑:

当时我在想也许有一个带有根节点和 4 个嵌套级别的树结构。每个级别代表代码中的一个位置。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 回答
1

我们可以将私有数映射到一个 3 维矩阵 P[x][y][z] 吗?

  • x 是私人的数量(2 ≤ n ≤ 65536)。
  • y 是介于 0 和 15 (0 - 9, a - f) 之间的标识符。
  • z 将是 1,包含标识符 (0 - f) 的选项。

例如:


牛肉
f00d

相应的 p[x][y] 将是

0 1 2 3 4 5 6 7 8 9 abcdef

0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1
1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1

z 维数为

0 0 0 0 0 0 0 0 0 2 0 0 9 4 0 (2 -> 0010, 9 -> 1001, 4 -> 0100)
0 0 0 0 0 0 0 0 0 0 8 0 0 6 1 (6 -> 0110, 8 -> 1000, 1 -> 0001)
6 0 0 0 0 0 0 0 0 0 0 0 1 0 8

我们逐列检查 p[x][y],如果 2 个海盗有相同的标识符,那么我们在他们的位置之间做一个“&”位运算并保存结果(可能需要另一个矩阵来决定这 2 个pirates 在当前列中的新匹配之前匹配,O(1))。

遍历这个矩阵一次可能就足够了 O(n)。

还没有通过代码证明,这是个好主意。希望能帮助到你。

感谢这个谜题。

于 2012-11-19T01:42:06.267 回答
1

该答案是对@fgb / @Daniel 提供的答案的详细说明。

让我们将每个标识符表示为H1H2H3H4(四个十六进制数字)

  1. 我们有四个数组,每个数组都是 length 16*16*16 (4096)。这四个数组将用于跟踪在 3 个位置相等的标识符。

  2. 对于读取的每个标识符,计算值(整数)H2H3H4, H1H3H4, H1H2H4, H1H2H3(请注意,这些值的范围是 0 - 4095;这就是我们选择数组大小为 4096 的原因)。现在,使用这些值中的每一个来索引步骤(1)中提到的四个数组,并将相应的位置加一。

  3. 我们还有另外六个数组,每个数组都是 length 16*16 (256)。这五个数组将用于跟踪在 2 个位置相等的标识符。

  4. 对于读取的每个标识符,计算值(整数)H3H4, H2H4, H1H4, H1H3, H1H2, H2H3。现在,使用这些值中的每一个来索引步骤(3)中提到的六个数组,并将相应的位置加一。每个数组位置有效地计算在 2 个位置相等的标识符的数量(但请注意,此计数也将包括在 3 个位置相等的标识符的数量)。

  5. 我们还有另外四个数组,每个数组都是 width 16。这四个数组将用于跟踪在 1 位置相等的标识符。

  6. 对于读取的每个标识符,计算值(整数)H4, H3, H2, H1。现在,使用这些值中的每一个来索引步骤(5)中提到的四个数组,并将相应的位置加一。每个数组位置有效地计算在 1 个位置相等的标识符的数量(同样,这个计数将包括在 3、2 个位置相等的标识符的数量)。

请注意,所有这些都可以通过输入一次完成。然而,这还不足以计算最终答案,我们必须在步骤 1 - 6 的同时逐步计算最终答案。我们可以这样做:

diff1 = 0
diff2 = 0
diff3 = 0
diff4 = 0
  • 在步骤 (2) 中,当我们增加某个数组位置时,如果结果值为u (u > 1),则diff1 = diff1 + (u - 1)。这意味着,使用(u - 1)已经找到的标识符,我们可以制作(u - 1)新的对(通过将它们中的每一个与当前标识符配对)。

  • 在步骤(4)中,当我们增加某个数组位置时,如果结果值为v (v - u >= 1)then diff2 = diff2 + (v - u)。这意味着,我们知道u在上一步中已经考虑了许多标识符,它们不应该被重新计算。因此,在2 个位置(v - u)给出与该标识符相等/不同的其他标识符。使用这些标识符,我们可以制作新的对(通过将它们中的每一个与当前标识符配对)。(v - u)

  • 在步骤 (6) 中,当我们增加某个数组位置时,如果结果值为w (w - u >= 1)then diff3 = diff3 + (w - u)。解释与上面的解释非常相似。

再次注意,所有这些步骤都可以在整个输入的单次通过期间完成。

现在,接下来是如何计算diff4。这可以在步骤 (2) 中完成,当我们增加某个数组位置时,如果新值为 1,那么diff4 = diff4 + 1 如果新值为 2,那么diff4 = diff4 - 1。这会跟踪迄今为止尚未找到匹配标识符的新标识符。

更新:上述方法效果很好,但我建议的计算方法diff4不起作用。基本上,不是关于唯一标识符,而是我们可以通过组合在所有 4 个位置上彼此diff4不同的标识符来形成多少对。

diff4在 C 中的以下实现中采用了不同的方法 (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 可以对此进行测试并查看它是否有效,那就太好了。它可能需要更多的工作。

更新:好的,它有效!必须重新格式化代码以使其通过那里的编译器。我还更新了此处列出的代码以反映更改。

于 2012-11-19T03:13:48.367 回答
0

你能尝试一下这些方面的东西吗:

当您处理每个条目时,将其添加到位置一有特定字母的名称列表中。列表中的任何条目都已添加到“4 个匹配候选者”列表中。

然后将其添加到具有特定第二个字母的名称列表中。此列表中已有的“4 个匹配候选者”中的任何条目仍保留在“4 个匹配候选者”中,“4 个匹配候选者”中的所有其他条目与该列表中的所有条目一起移动到“3 个匹配候选者”列表中.

然后将其添加到具有特定第三个字母的名称列表中。此列表中已有的“3 个匹配候选者”中的任何条目仍保留在“3 个匹配候选者”中,“3 个匹配候选者”中的所有其他条目与该列表中已有的所有条目一起移动到“2 个匹配候选者”列表中列表。此列表中“4 个匹配候选者”中的任何条目都保留在“4 个匹配候选者”中,“4 个匹配候选者”中的所有其他条目都移动到“3 个匹配候选者”列表中。

然后将其添加到具有特定第四个字母的名称列表中。此列表中的任何条目都添加到 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 回答