jus*_*tyy 16 java string algorithm
我一直试图在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]);
}
}
Run Code Online (Sandbox Code Playgroud)
有什么想让这种方法更快?我注意到输入中只允许一组有限的字符('0'..'9','a'..'f')所以我们可以创建数组(内存限制就足够了)来快速检查之前已输入过字符.
谢谢......我不需要实际的实施,只是快速的想法/想法会很棒. 编辑:谢谢你的好主意.我已尝试使用位逻辑对O(n ^ 2)进行改进,但仍超出时间限制.pascal代码如下.
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.
Run Code Online (Sandbox Code Playgroud)
所以我想,我会尝试其他更好的建议..
编辑: 我已经尝试了丹尼尔的算法,这是非常有希望的,也许在下面的代码中有一个错误,它在测试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);
}
}
Run Code Online (Sandbox Code Playgroud)
编辑 终于得到了AC ...感谢Daniel这么棒的算法!
对于小型n
,O(n²)
当然,检查每对的蛮力算法更快,因此人们希望找到切换算法的良好截止点.在没有测量的情况下,由于不均匀的包络考虑,我预计值在200到3000之间.
通过将盗版ID int
解析为十六进制数来将其转换为a.存储ID
int[] pirates = new int[n];
Run Code Online (Sandbox Code Playgroud)
首先,计算具有相同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;
}
Run Code Online (Sandbox Code Playgroud)
接下来,计算他们的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;
}
Run Code Online (Sandbox Code Playgroud)
但是,每对具有四个相同半字节的盗贼4 choose 3 = 4
在这里计算时间,对于每个可能选择的三个半字节,所以我们需要减去它(好吧,不是问题,但原则上):
threeIdentical -= 4*fourIdentical;
Run Code Online (Sandbox Code Playgroud)
然后,计算他们的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;
}
Run Code Online (Sandbox Code Playgroud)
分配的256个六个阵列int
S和计数海盗与相应的半字节中的位数a
和b
,像
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
Run Code Online (Sandbox Code Playgroud)
但是,具有四个相同半字节的对在4 choose 2 = 6
这里已被计数次数,并且具有三个相同半字节的对已被计数3 choose 2 = 3
次数,因此我们需要减去
twoIdentical -= 6*fourIdentical + 3*threeIdentical;
Run Code Online (Sandbox Code Playgroud)
接下来,具有一个相同半字节的对的数量.我相信你可以猜到需要四个函数和数组.然后我们将计算具有四个相同半字节4 choose 1 = 4
时间的对,具有三个相同半字节3 choose 1 = 3
时间的对,以及具有两个相同半字节2 choose 1 = 2
时间的对,所以
oneIdentical -= 4*fourIdentical + 3*threeIdentical + 2*twoIdentical;
Run Code Online (Sandbox Code Playgroud)
最后,没有相同半字节的对的数量是
int noneIdentical = (int)((long)n*(n-1) / 2) - oneIdentical - twoIdentical - threeIdentical - fourIdentical;
Run Code Online (Sandbox Code Playgroud)
(施法者long
要避免溢出n*(n-1)
).
归档时间: |
|
查看次数: |
909 次 |
最近记录: |