字符串
问题:为文档中包含的单词生成一个列表
使用模板sets和strings
int main(int argc, char **argv)
{
set<string> str;
set<string>::iterator iter;
string t;
while(cin >> t)
{
str.insert(t);
}
for(iter = str.begin(); iter != str.end(); iter++)
{
cout << *iter << endl;
}
return 0;
}
问题:出现的单词计数
方法一:使用标准模板中的map
int main(int argc, char **argv)
{
map<string, int> m;
map<string, int>::iterator iter;
string t;
while(cin >> t)
{
m[t]++;
}
for(iter = m.begin(); iter != m.end(); iter++)
{
cout << iter->first << " " << iter->second << endl;
}
return 0;
}
方法二:减少时间,自己建立列表
typedef struct node *nodeptr;
typedef struct node
{
char *word;
int count;
nodeptr next;
}node;
#define NHASH 29989 //圣经中有29131个不同的单词,用跟29131最接近的质数作为散列表的大小
#define MULT 31 //hash时用到的乘数
nodeptr bin[NHASH]; //散列表
unsigned int hash(char *p)
{
unsigned int h = 0;
for( ; *p != ''; p++)
{
h = MULT * h + *p;
}
return h % NHASH;
}
#define NODEGROUP 1000
int nodesleft = 0;
nodeptr freenode;
nodeptr nmalloc()
{
if(nodesleft == 0)
{
freenode = (nodeptr)malloc(NODEGROUP *sizeof(node));
nodesleft = NODEGROUP;
}
nodesleft--;
return freenode++;
}
#define CHARGROUP 10000
int charleft = 0;
char *freechar;
char *smalloc(int n)
{
if(charleft < n)
{
freechar = (char *)malloc(n + CHARGROUP);
charleft = n + CHARGROUP;
}
charleft -= n;
freechar += n;
return freechar - n;
}
//增加原先存在的单词的计数值,若之前没有该单词,则对计数器初始化
void incword(char *s)
{
nodeptr p;
int h = hash(s); //找到与单词对应的箱
for(p = bin[h]; p != NULL; p = p->next)
{
if(strcmp(s, p->word) == 0)
{
(p->count)++;
return;
}
}
p = nmalloc();
p->count = 1;
p->word = smalloc(strlen(s) + 1);
strcpy(p->word, s);
p->next = bin[h]; //头插法
bin[h] = p;
}
int main()
{
int i;
nodeptr p;
char buf[100];
for(i = 0; i < NHASH; i++)
{
bin[i] = NULL;
}
while(scanf("%s", buf) != EOF)
{
incword(buf);
}
for(i = 0; i < NHASH; i++)
{
for(p = bin[i]; p != NULL; p = p->next)
{
printf("%s %dn", p->word, p->count);
}
}
return 0;
}
前面介绍了表示单词集合的两种方法。平衡搜索树将字符串看作是不可分割的对象进行操作,标准模板库的set和map中大部分实现都使用这种结构。平衡搜索树中的元素始终处于有序状态,从而很容易的指向寻找前驱结点或按顺序输出元素之类的操作。另一方面,散列则需要深入字符串的内部,计算散列函数并将关键字散列到一个较大的表中。散列方法的平均速度很快,但缺乏平衡树提供的最坏情况的保证,也不能支持其他涉及顺序的操作。
问题:给定文本输入,计算其中最长的重复子字符串。
方法一:双重for比较每个字符串,找到最长重复字符串
//找到两个字符串公共部分的长度
int comlen(char *p, char *q)
{
int i = 0;
while (*p && (*p++ == *q++))
{
i++;
}
return i;
}
int main()
{
int i, j;
int maxi, maxj;
int currentlen, maxlen = -1;
char *str = "ask not what your country can do for you, but what you can do for your country";
int length = strlen(str);
for(i = 0; i < length; i++)
{
for(j = i + 1; j < length; j++)
{
currentlen = comlen(str + i, str + j);
if(currentlen > maxlen)
{
maxlen = currentlen;
maxi = i;
maxj = j;
}
}
}
for(i = 0; i < maxlen; i++)
{
printf("%c", str[maxi + i]);
}
printf("n");
return 0;
}
方法二:采用后缀数组处理
#define MAXN 50000
char c[MAXN], *a[MAXN];
while(ch = getchar() != EOF)
{
a[n] = &c[n];
c[n] = ch;
}
c[n] = 0;
元素a[0]指向整个字符串,下一个元素指向从第二个字符开始的数组后缀,等等。对于输入字符串“banana”,该数组能够表示如下后缀:
char *a="banana"
a[0]=banana
a[1]=anana
a[2]=nana
a[3]=ana
a[4]=na
a[5]=a
如果某个长字符串在数组c中出现了两次,那么它将出现在两个不同的后缀中,因此我们队数组进行排序以寻找相同的后缀。“banana”数组排序为:
a[0]=a
a[1]=ana
a[2]=anana
a[3]=banana
a[4]=na
a[5]=nana
然后就可以扫描数组,通过比较相邻元素来找出最长的重复字符串。
该方法由于排序的存在,时间复杂度为O(nlogn)。
程序:
int pstrcmp(char **p, char **q)
{ return strcmp(*p, *q); }//只能比较字符串,两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇''为止。
int comlen(char *p, char *q)//返回两个参数共同部分的长度
{ int i = 0;
while (*p && (*p++ == *q++))
i++;
return i;
}
#define M 1
#define MAXN 5000000
char c[MAXN], *a[MAXN];
int main()
{ int i, ch, n = 0, maxi, maxlen = -1;
while ((ch = getchar()) != EOF) {
a[n] = &c[n];
c[n++] = ch;
}
c[n] = 0;
qsort(a, n, sizeof(char *), pstrcmp);//快速排序
for (i = 0; i < n-M; i++)
if (comlen(a[i], a[i+M]) > maxlen) {//比较相邻字符串相同个数
maxlen = comlen(a[i], a[i+M]);
maxi = i;
}
printf("%.*sn", maxlen, a[maxi]);
return 0;
}
问题:生成随机文本
基于字母:下一个字母设置为前k个字母的随机函数
char x[5000000];
int main()
{ int c, i, eqsofar, max, n = 0, k = 5;
char *p, *nextp, *q;
while ((c = getchar()) != EOF)
{
x[n++] = c;
}
x[n] = 0;
p = x;
srand(1);
for (max = 2000; max > 0; max--)
{
eqsofar = 0;
for (q = x; q < x + n - k + 1; q++)
{
for (i = 0; i < k && *(p+i) == *(q+i); i++)
;
if (i == k)
{
if (rand() % ++eqsofar == 0)
{
nextp = q;
}
}
}
c = *(nextp+k);
if (c == 0)
{
break;
}
putchar(c);
p = nextp+1;
}
return 0;
}
基于单词:
1.随机输出字典中的单词
2.读取一个文档,根据适当的概率选择下一个输出的单词
3.生成下一个单词考虑前几个单词的马尔科夫链
香农的算法:以构建【字母级别的一阶文本】为例,随机打开一本书并在该页随机选择一个字母记录下来。然后翻到另一页开始读,直到遇到该字母,此时记录喜爱其后面的那个字母。再翻到另外一页搜索上述第二个字母并记录其后面的那个字母。依次类推。对于【字母级别的1阶、2阶文本和单词级别的0阶、1阶文本】,处理过程类似。
代码:
char inputchars[4300000];
char *word[800000];
int nword = 0;
int k = 2;
int wordncmp(char *p, char* q)
{
int n = k;
for ( ; *p == *q; p++, q++)
{
if (*p == 0 && --n == 0)
return 0;
}
return *p - *q;
}
int sortcmp(char **p, char **q)
{
return wordncmp(*p, *q);
}
char *skip(char *p, int n)
{ for ( ; n > 0; p++)
{
if (*p == 0)
n--;
}
return p;
}
int main()
{
int i, wordsleft = 10000, l, m, u;
char *phrase, *p;
word[0] = inputchars;
while (scanf("%s", word[nword]) != EOF)
{
word[nword+1] = word[nword] + strlen(word[nword]) + 1;
nword++;
}
for (i = 0; i < k; i++)
word[nword][i] = 0;
for (i = 0; i < k; i++)
printf("%sn", word[i]);
qsort(word, nword, sizeof(word[0]), sortcmp);
phrase = inputchars;
for ( ; wordsleft > 0; wordsleft--)
{
l = -1;
u = nword;
while (l+1 != u)
{
m = (l + u) / 2;
if (wordncmp(word[m], phrase) < 0)
l = m;
else
u = m;
}
for (i = 0; wordncmp(phrase, word[u+i]) == 0; i++)
{
if (rand() % (i+1) == 0)
{
p = word[u+i];
}
}
phrase = skip(p, 1);
if (strlen(skip(phrase, k-1)) == 0)
{
break;
}
printf("%sn", skip(phrase, k-1));
}
return 0;
}